#include "stdafx.h" #include "rpcs3/Ini.h" #include "Utilities/rPlatform.h" // only for rImage #include "Utilities/rFile.h" #include "Utilities/Log.h" #include "Emu/Memory/Memory.h" #include "Emu/System.h" #include "GLGSRender.h" GetGSFrameCb GetGSFrame = nullptr; void SetGetGSFrameCallback(GetGSFrameCb value) { GetGSFrame = value; } #define CMD_DEBUG 0 #define DUMP_VERTEX_DATA 0 #if CMD_DEBUG #define CMD_LOG(...) LOG_NOTICE(RSX, __VA_ARGS__) #else #define CMD_LOG(...) #endif GLuint g_flip_tex, g_depth_tex, g_pbo[6]; 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) { LOG_ERROR(RSX, "%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 void GLTexture::Create() { if (m_id) { Delete(); } if (!m_id) { glGenTextures(1, &m_id); checkForGlError("GLTexture::Init() -> glGenTextures"); Bind(); } } int GLTexture::GetGlWrap(int wrap) { switch (wrap) { case CELL_GCM_TEXTURE_WRAP: return GL_REPEAT; case CELL_GCM_TEXTURE_MIRROR: return GL_MIRRORED_REPEAT; case CELL_GCM_TEXTURE_CLAMP_TO_EDGE: return GL_CLAMP_TO_EDGE; case CELL_GCM_TEXTURE_BORDER: return GL_CLAMP_TO_BORDER; case CELL_GCM_TEXTURE_CLAMP: return GL_CLAMP_TO_EDGE; case CELL_GCM_TEXTURE_MIRROR_ONCE_CLAMP_TO_EDGE: return GL_MIRROR_CLAMP_TO_EDGE_EXT; case CELL_GCM_TEXTURE_MIRROR_ONCE_BORDER: return GL_MIRROR_CLAMP_TO_BORDER_EXT; case CELL_GCM_TEXTURE_MIRROR_ONCE_CLAMP: return GL_MIRROR_CLAMP_EXT; } LOG_ERROR(RSX, "Texture wrap error: bad wrap (%d).", wrap); return GL_REPEAT; } float GLTexture::GetMaxAniso(int aniso) { switch (aniso) { case CELL_GCM_TEXTURE_MAX_ANISO_1: return 1.0f; case CELL_GCM_TEXTURE_MAX_ANISO_2: return 2.0f; case CELL_GCM_TEXTURE_MAX_ANISO_4: return 4.0f; case CELL_GCM_TEXTURE_MAX_ANISO_6: return 6.0f; case CELL_GCM_TEXTURE_MAX_ANISO_8: return 8.0f; case CELL_GCM_TEXTURE_MAX_ANISO_10: return 10.0f; case CELL_GCM_TEXTURE_MAX_ANISO_12: return 12.0f; case CELL_GCM_TEXTURE_MAX_ANISO_16: return 16.0f; } LOG_ERROR(RSX, "Texture anisotropy error: bad max aniso (%d).", aniso); return 1.0f; } void GLTexture::Init(RSXTexture& tex) { if (tex.GetLocation() > 1) { return; } Bind(); const u64 texaddr = GetAddress(tex.GetOffset(), tex.GetLocation()); if (!Memory.IsGoodAddr(texaddr)) { LOG_ERROR(RSX, "Bad texture address=0x%x", texaddr); return; } //lOG_WARNING(RSX, "texture addr = 0x%x, width = %d, height = %d, max_aniso=%d, mipmap=%d, remap=0x%x, zfunc=0x%x, wraps=0x%x, wrapt=0x%x, wrapr=0x%x, minlod=0x%x, maxlod=0x%x", // m_offset, m_width, m_height, m_maxaniso, m_mipmap, m_remap, m_zfunc, m_wraps, m_wrapt, m_wrapr, m_minlod, m_maxlod); //TODO: safe init checkForGlError("GLTexture::Init() -> glBindTexture"); int format = tex.GetFormat() & ~(CELL_GCM_TEXTURE_LN | CELL_GCM_TEXTURE_UN); bool is_swizzled = !(tex.GetFormat() & CELL_GCM_TEXTURE_LN); auto pixels = vm::get_ptr(texaddr); u8 *unswizzledPixels; static const GLint glRemapStandard[4] = { GL_ALPHA, GL_RED, GL_GREEN, GL_BLUE }; // NOTE: This must be in ARGB order in all forms below. const GLint *glRemap = glRemapStandard; switch (format) { case CELL_GCM_TEXTURE_B8: // One 8-bit fixed-point number { glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex.GetWidth(), tex.GetHeight(), 0, GL_BLUE, GL_UNSIGNED_BYTE, pixels); checkForGlError("GLTexture::Init() -> glTexImage2D(CELL_GCM_TEXTURE_B8)"); static const GLint swizzleMaskB8[] = { GL_BLUE, GL_BLUE, GL_BLUE, GL_BLUE }; glRemap = swizzleMaskB8; } break; case CELL_GCM_TEXTURE_A1R5G5B5: glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_TRUE); checkForGlError("GLTexture::Init() -> glPixelStorei"); // TODO: texture swizzling glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex.GetWidth(), tex.GetHeight(), 0, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV, pixels); checkForGlError("GLTexture::Init() -> glTexImage2D(CELL_GCM_TEXTURE_A1R5G5B5)"); glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_FALSE); checkForGlError("GLTexture::Init() -> glPixelStorei(CELL_GCM_TEXTURE_A1R5G5B5)"); break; case CELL_GCM_TEXTURE_A4R4G4B4: { glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex.GetWidth(), tex.GetHeight(), 0, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, pixels); checkForGlError("GLTexture::Init() -> glTexImage2D(CELL_GCM_TEXTURE_A4R4G4B4)"); // We read it in as R4G4B4A4, so we need to remap each component. static const GLint swizzleMaskA4R4G4B4[] = { GL_BLUE, GL_ALPHA, GL_RED, GL_GREEN }; glRemap = swizzleMaskA4R4G4B4; } break; case CELL_GCM_TEXTURE_R5G6B5: { glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_TRUE); checkForGlError("GLTexture::Init() -> glPixelStorei(CELL_GCM_TEXTURE_R5G6B5)"); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, tex.GetWidth(), tex.GetHeight(), 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, pixels); checkForGlError("GLTexture::Init() -> glTexImage2D(CELL_GCM_TEXTURE_R5G6B5)"); glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_FALSE); checkForGlError("GLTexture::Init() -> glPixelStorei(CELL_GCM_TEXTURE_R5G6B5)"); } break; case CELL_GCM_TEXTURE_A8R8G8B8: { if (is_swizzled) { u32 *src, *dst; u32 log2width, log2height; unswizzledPixels = (u8*)malloc(tex.GetWidth() * tex.GetHeight() * 4); src = (u32*)pixels; dst = (u32*)unswizzledPixels; log2width = log(tex.GetWidth()) / log(2); log2height = log(tex.GetHeight()) / log(2); for (int i = 0; i < tex.GetHeight(); i++) { for (int j = 0; j < tex.GetWidth(); j++) { dst[(i*tex.GetHeight()) + j] = src[LinearToSwizzleAddress(j, i, 0, log2width, log2height, 0)]; } } } glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex.GetWidth(), tex.GetHeight(), 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8, is_swizzled ? unswizzledPixels : pixels); checkForGlError("GLTexture::Init() -> glTexImage2D(CELL_GCM_TEXTURE_A8R8G8B8)"); } break; case CELL_GCM_TEXTURE_COMPRESSED_DXT1: // Compressed 4x4 pixels into 8 bytes { u32 size = ((tex.GetWidth() + 3) / 4) * ((tex.GetHeight() + 3) / 4) * 8; glCompressedTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, tex.GetWidth(), tex.GetHeight(), 0, size, pixels); checkForGlError("GLTexture::Init() -> glCompressedTexImage2D(CELL_GCM_TEXTURE_COMPRESSED_DXT1)"); } break; case CELL_GCM_TEXTURE_COMPRESSED_DXT23: // Compressed 4x4 pixels into 16 bytes { u32 size = ((tex.GetWidth() + 3) / 4) * ((tex.GetHeight() + 3) / 4) * 16; glCompressedTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, tex.GetWidth(), tex.GetHeight(), 0, size, pixels); checkForGlError("GLTexture::Init() -> glCompressedTexImage2D(CELL_GCM_TEXTURE_COMPRESSED_DXT23)"); } break; case CELL_GCM_TEXTURE_COMPRESSED_DXT45: // Compressed 4x4 pixels into 16 bytes { u32 size = ((tex.GetWidth() + 3) / 4) * ((tex.GetHeight() + 3) / 4) * 16; glCompressedTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, tex.GetWidth(), tex.GetHeight(), 0, size, pixels); checkForGlError("GLTexture::Init() -> glCompressedTexImage2D(CELL_GCM_TEXTURE_COMPRESSED_DXT45)"); } break; case CELL_GCM_TEXTURE_G8B8: { glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex.GetWidth(), tex.GetHeight(), 0, GL_RG, GL_UNSIGNED_BYTE, pixels); checkForGlError("GLTexture::Init() -> glTexImage2D(CELL_GCM_TEXTURE_G8B8)"); static const GLint swizzleMaskG8B8[] = { GL_RED, GL_GREEN, GL_RED, GL_GREEN }; glRemap = swizzleMaskG8B8; } break; case CELL_GCM_TEXTURE_R6G5B5: { // TODO: Probably need to actually unswizzle if is_swizzled. const u32 numPixels = tex.GetWidth() * tex.GetHeight(); unswizzledPixels = (u8 *)malloc(numPixels * 4); // TODO: Speed. for (u32 i = 0; i < numPixels; ++i) { u16 c = reinterpret_cast *>(pixels)[i]; unswizzledPixels[i * 4 + 0] = Convert6To8((c >> 10) & 0x3F); unswizzledPixels[i * 4 + 1] = Convert5To8((c >> 5) & 0x1F); unswizzledPixels[i * 4 + 2] = Convert5To8((c >> 0) & 0x1F); unswizzledPixels[i * 4 + 3] = 255; } glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex.GetWidth(), tex.GetHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, unswizzledPixels); checkForGlError("GLTexture::Init() -> glTexImage2D(CELL_GCM_TEXTURE_R6G5B5)"); free(unswizzledPixels); } break; case CELL_GCM_TEXTURE_DEPTH24_D8: // 24-bit unsigned fixed-point number and 8 bits of garbage { glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, tex.GetWidth(), tex.GetHeight(), 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, pixels); checkForGlError("GLTexture::Init() -> glTexImage2D(CELL_GCM_TEXTURE_DEPTH24_D8)"); } break; case CELL_GCM_TEXTURE_DEPTH24_D8_FLOAT: // 24-bit unsigned float and 8 bits of garbage { glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, tex.GetWidth(), tex.GetHeight(), 0, GL_DEPTH_COMPONENT, GL_FLOAT, pixels); checkForGlError("GLTexture::Init() -> glTexImage2D(CELL_GCM_TEXTURE_DEPTH24_D8_FLOAT)"); } break; case CELL_GCM_TEXTURE_DEPTH16: // 16-bit unsigned fixed-point number { glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT16, tex.GetWidth(), tex.GetHeight(), 0, GL_DEPTH_COMPONENT, GL_SHORT, pixels); checkForGlError("GLTexture::Init() -> glTexImage2D(CELL_GCM_TEXTURE_DEPTH16)"); } break; case CELL_GCM_TEXTURE_DEPTH16_FLOAT: // 16-bit unsigned float { glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT16, tex.GetWidth(), tex.GetHeight(), 0, GL_DEPTH_COMPONENT, GL_FLOAT, pixels); checkForGlError("GLTexture::Init() -> glTexImage2D(CELL_GCM_TEXTURE_DEPTH16_FLOAT)"); } break; case CELL_GCM_TEXTURE_X16: // A 16-bit fixed-point number { glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_TRUE); checkForGlError("GLTexture::Init() -> glPixelStorei(CELL_GCM_TEXTURE_X16)"); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex.GetWidth(), tex.GetHeight(), 0, GL_RED, GL_UNSIGNED_SHORT, pixels); checkForGlError("GLTexture::Init() -> glTexImage2D(CELL_GCM_TEXTURE_X16)"); glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_FALSE); checkForGlError("GLTexture::Init() -> glPixelStorei(CELL_GCM_TEXTURE_X16)"); static const GLint swizzleMaskX16[] = { GL_RED, GL_ONE, GL_RED, GL_ONE }; glRemap = swizzleMaskX16; } break; case CELL_GCM_TEXTURE_Y16_X16: // Two 16-bit fixed-point numbers { glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_TRUE); checkForGlError("GLTexture::Init() -> glPixelStorei(CELL_GCM_TEXTURE_Y16_X16)"); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex.GetWidth(), tex.GetHeight(), 0, GL_RG, GL_UNSIGNED_SHORT, pixels); checkForGlError("GLTexture::Init() -> glTexImage2D(CELL_GCM_TEXTURE_Y16_X16)"); glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_FALSE); checkForGlError("GLTexture::Init() -> glPixelStorei(CELL_GCM_TEXTURE_Y16_X16)"); static const GLint swizzleMaskX32_Y16_X16[] = { GL_GREEN, GL_RED, GL_GREEN, GL_RED }; glRemap = swizzleMaskX32_Y16_X16; } break; case CELL_GCM_TEXTURE_R5G5B5A1: { glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_TRUE); checkForGlError("GLTexture::Init() -> glPixelStorei(CELL_GCM_TEXTURE_R5G5B5A1)"); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex.GetWidth(), tex.GetHeight(), 0, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, pixels); checkForGlError("GLTexture::Init() -> glTexImage2D(CELL_GCM_TEXTURE_R5G5B5A1)"); glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_FALSE); checkForGlError("GLTexture::Init() -> glPixelStorei(CELL_GCM_TEXTURE_R5G5B5A1)"); } break; case CELL_GCM_TEXTURE_W16_Z16_Y16_X16_FLOAT: // Four fp16 values { glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_TRUE); checkForGlError("GLTexture::Init() -> glPixelStorei(CELL_GCM_TEXTURE_W16_Z16_Y16_X16_FLOAT)"); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex.GetWidth(), tex.GetHeight(), 0, GL_RGBA, GL_HALF_FLOAT, pixels); checkForGlError("GLTexture::Init() -> glTexImage2D(CELL_GCM_TEXTURE_W16_Z16_Y16_X16_FLOAT)"); glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_FALSE); checkForGlError("GLTexture::Init() -> glPixelStorei(CELL_GCM_TEXTURE_W16_Z16_Y16_X16_FLOAT)"); } break; case CELL_GCM_TEXTURE_W32_Z32_Y32_X32_FLOAT: // Four fp32 values { glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex.GetWidth(), tex.GetHeight(), 0, GL_BGRA, GL_FLOAT, pixels); checkForGlError("GLTexture::Init() -> glTexImage2D(CELL_GCM_TEXTURE_W32_Z32_Y32_X32_FLOAT)"); } break; case CELL_GCM_TEXTURE_X32_FLOAT: // One 32-bit floating-point number { glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex.GetWidth(), tex.GetHeight(), 0, GL_RED, GL_FLOAT, pixels); checkForGlError("GLTexture::Init() -> glTexImage2D(CELL_GCM_TEXTURE_X32_FLOAT)"); static const GLint swizzleMaskX32_FLOAT[] = { GL_RED, GL_ONE, GL_ONE, GL_ONE }; glRemap = swizzleMaskX32_FLOAT; } break; case CELL_GCM_TEXTURE_D1R5G5B5: { glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_TRUE); checkForGlError("GLTexture::Init() -> glPixelStorei(CELL_GCM_TEXTURE_D1R5G5B5)"); // TODO: Texture swizzling glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex.GetWidth(), tex.GetHeight(), 0, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV, pixels); checkForGlError("GLTexture::Init() -> glTexImage2D(CELL_GCM_TEXTURE_D1R5G5B5)"); static const GLint swizzleMaskX32_D1R5G5B5[] = { GL_ONE, GL_RED, GL_GREEN, GL_BLUE }; glRemap = swizzleMaskX32_D1R5G5B5; glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_FALSE); checkForGlError("GLTexture::Init() -> glPixelStorei(CELL_GCM_TEXTURE_D1R5G5B5)"); } break; case CELL_GCM_TEXTURE_D8R8G8B8: // 8 bits of garbage and three unsigned 8-bit fixed-point numbers { glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex.GetWidth(), tex.GetHeight(), 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8, pixels); checkForGlError("GLTexture::Init() -> glTexImage2D(CELL_GCM_TEXTURE_D8R8G8B8)"); static const GLint swizzleMaskX32_D8R8G8B8[] = { GL_ONE, GL_RED, GL_GREEN, GL_BLUE }; glRemap = swizzleMaskX32_D8R8G8B8; } break; case CELL_GCM_TEXTURE_Y16_X16_FLOAT: // Two fp16 values { glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_TRUE); checkForGlError("GLTexture::Init() -> glPixelStorei(CELL_GCM_TEXTURE_Y16_X16_FLOAT)"); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex.GetWidth(), tex.GetHeight(), 0, GL_RG, GL_HALF_FLOAT, pixels); checkForGlError("GLTexture::Init() -> glTexImage2D(CELL_GCM_TEXTURE_Y16_X16_FLOAT)"); glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_FALSE); checkForGlError("GLTexture::Init() -> glPixelStorei(CELL_GCM_TEXTURE_Y16_X16_FLOAT)"); static const GLint swizzleMaskX32_Y16_X16_FLOAT[] = { GL_RED, GL_GREEN, GL_RED, GL_GREEN }; glRemap = swizzleMaskX32_Y16_X16_FLOAT; } break; case CELL_GCM_TEXTURE_COMPRESSED_B8R8_G8R8 & ~(CELL_GCM_TEXTURE_LN | CELL_GCM_TEXTURE_UN): { const u32 numPixels = tex.GetWidth() * tex.GetHeight(); unswizzledPixels = (u8 *)malloc(numPixels * 4); // TODO: Speed. for (u32 i = 0; i < numPixels; i += 2) { unswizzledPixels[i * 4 + 0 + 0] = pixels[i * 2 + 3]; unswizzledPixels[i * 4 + 0 + 1] = pixels[i * 2 + 2]; unswizzledPixels[i * 4 + 0 + 2] = pixels[i * 2 + 0]; unswizzledPixels[i * 4 + 0 + 3] = 255; // The second pixel is the same, except for red. unswizzledPixels[i * 4 + 4 + 0] = pixels[i * 2 + 1]; unswizzledPixels[i * 4 + 4 + 1] = pixels[i * 2 + 2]; unswizzledPixels[i * 4 + 4 + 2] = pixels[i * 2 + 0]; unswizzledPixels[i * 4 + 4 + 3] = 255; } glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex.GetWidth(), tex.GetHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, unswizzledPixels); checkForGlError("GLTexture::Init() -> glTexImage2D(CELL_GCM_TEXTURE_COMPRESSED_B8R8_G8R8 & ~(CELL_GCM_TEXTURE_LN | CELL_GCM_TEXTURE_UN)"); free(unswizzledPixels); } break; case CELL_GCM_TEXTURE_COMPRESSED_R8B8_R8G8 & ~(CELL_GCM_TEXTURE_LN | CELL_GCM_TEXTURE_UN): { const u32 numPixels = tex.GetWidth() * tex.GetHeight(); unswizzledPixels = (u8 *)malloc(numPixels * 4); // TODO: Speed. for (u32 i = 0; i < numPixels; i += 2) { unswizzledPixels[i * 4 + 0 + 0] = pixels[i * 2 + 2]; unswizzledPixels[i * 4 + 0 + 1] = pixels[i * 2 + 3]; unswizzledPixels[i * 4 + 0 + 2] = pixels[i * 2 + 1]; unswizzledPixels[i * 4 + 0 + 3] = 255; // The second pixel is the same, except for red. unswizzledPixels[i * 4 + 4 + 0] = pixels[i * 2 + 0]; unswizzledPixels[i * 4 + 4 + 1] = pixels[i * 2 + 3]; unswizzledPixels[i * 4 + 4 + 2] = pixels[i * 2 + 1]; unswizzledPixels[i * 4 + 4 + 3] = 255; } glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex.GetWidth(), tex.GetHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, unswizzledPixels); checkForGlError("GLTexture::Init() -> glTexImage2D(CELL_GCM_TEXTURE_COMPRESSED_R8B8_R8G8 & ~(CELL_GCM_TEXTURE_LN | CELL_GCM_TEXTURE_UN)"); free(unswizzledPixels); } break; default: LOG_ERROR(RSX, "Init tex error: Bad tex format (0x%x | %s | 0x%x)", format, (is_swizzled ? "swizzled" : "linear"), tex.GetFormat() & 0x40); break; } glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, tex.GetMipmap() - 1); glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, tex.GetMipmap() > 1); if (format != CELL_GCM_TEXTURE_B8 && format != CELL_GCM_TEXTURE_X16 && format != CELL_GCM_TEXTURE_X32_FLOAT) { u8 remap_a = tex.GetRemap() & 0x3; u8 remap_r = (tex.GetRemap() >> 2) & 0x3; u8 remap_g = (tex.GetRemap() >> 4) & 0x3; u8 remap_b = (tex.GetRemap() >> 6) & 0x3; glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, glRemap[remap_a]); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, glRemap[remap_r]); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G, glRemap[remap_g]); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, glRemap[remap_b]); } else { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, glRemap[0]); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, glRemap[1]); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G, glRemap[2]); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, glRemap[3]); } checkForGlError("GLTexture::Init() -> remap"); static const int gl_tex_zfunc[] = { GL_NEVER, GL_LESS, GL_EQUAL, GL_LEQUAL, GL_GREATER, GL_NOTEQUAL, GL_GEQUAL, GL_ALWAYS, }; glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GetGlWrap(tex.GetWrapS())); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GetGlWrap(tex.GetWrapT())); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GetGlWrap(tex.GetWrapR())); checkForGlError("GLTexture::Init() -> wrap"); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, gl_tex_zfunc[tex.GetZfunc()]); checkForGlError("GLTexture::Init() -> compare"); glTexEnvi(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, tex.GetBias()); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_LOD, (tex.GetMinLOD() >> 8)); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LOD, (tex.GetMaxLOD() >> 8)); checkForGlError("GLTexture::Init() -> lod"); static const int gl_tex_min_filter[] = { GL_NEAREST, // unused GL_NEAREST, GL_LINEAR, GL_NEAREST_MIPMAP_NEAREST, GL_LINEAR_MIPMAP_NEAREST, GL_NEAREST_MIPMAP_LINEAR, GL_LINEAR_MIPMAP_LINEAR, GL_NEAREST, // CELL_GCM_TEXTURE_CONVOLUTION_MIN }; static const int gl_tex_mag_filter[] = { GL_NEAREST, // unused GL_NEAREST, GL_LINEAR, GL_NEAREST, // unused GL_LINEAR // CELL_GCM_TEXTURE_CONVOLUTION_MAG }; glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_tex_min_filter[tex.GetMinFilter()]); checkForGlError("GLTexture::Init() -> min filters"); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_tex_mag_filter[tex.GetMagFilter()]); checkForGlError("GLTexture::Init() -> mag filters"); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, GetMaxAniso(tex.GetMaxAniso())); checkForGlError("GLTexture::Init() -> max anisotropy"); //Unbind(); if (is_swizzled && format == CELL_GCM_TEXTURE_A8R8G8B8) { free(unswizzledPixels); } } void GLTexture::Save(RSXTexture& tex, const std::string& name) { if (!m_id || !tex.GetOffset() || !tex.GetWidth() || !tex.GetHeight()) return; const u32 texPixelCount = tex.GetWidth() * tex.GetHeight(); u32* alldata = new u32[texPixelCount]; Bind(); switch (tex.GetFormat() & ~(0x20 | 0x40)) { case CELL_GCM_TEXTURE_B8: glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, alldata); break; case CELL_GCM_TEXTURE_A8R8G8B8: glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, alldata); break; default: delete[] alldata; return; } { rFile f(name + ".raw", rFile::write); f.Write(alldata, texPixelCount * 4); } u8* data = new u8[texPixelCount * 3]; u8* alpha = new u8[texPixelCount]; u8* src = (u8*)alldata; u8* dst_d = data; u8* dst_a = alpha; for (u32 i = 0; i < texPixelCount; i++) { *dst_d++ = *src++; *dst_d++ = *src++; *dst_d++ = *src++; *dst_a++ = *src++; } rImage out; out.Create(tex.GetWidth(), tex.GetHeight(), data, alpha); out.SaveFile(name, rBITMAP_TYPE_PNG); delete[] alldata; //free(data); //free(alpha); } void GLTexture::Save(RSXTexture& tex) { static const std::string& dir_path = "textures"; static const std::string& file_fmt = dir_path + "/" + "tex[%d].png"; if (!rExists(dir_path)) rMkdir(dir_path); u32 count = 0; while (rExists(fmt::Format(file_fmt.c_str(), count))) count++; Save(tex, fmt::Format(file_fmt.c_str(), count)); } void GLTexture::Bind() { glBindTexture(GL_TEXTURE_2D, m_id); } void GLTexture::Unbind() { glBindTexture(GL_TEXTURE_2D, 0); } void GLTexture::Delete() { if (m_id) { glDeleteTextures(1, &m_id); m_id = 0; } } void PostDrawObj::Draw() { static bool s_is_initialized = false; if (!s_is_initialized) { s_is_initialized = true; Initialize(); } else { m_program.Use(); } } void PostDrawObj::Initialize() { InitializeShaders(); m_fp.Compile(); m_vp.Compile(); m_program.Create(m_vp.id, m_fp.GetId()); m_program.Use(); InitializeLocations(); } void DrawCursorObj::Draw() { checkForGlError("PostDrawObj : Unknown error."); PostDrawObj::Draw(); checkForGlError("PostDrawObj::Draw"); if (!m_fbo.IsCreated()) { m_fbo.Create(); checkForGlError("DrawCursorObj : m_fbo.Create"); m_fbo.Bind(); checkForGlError("DrawCursorObj : m_fbo.Bind"); m_rbo.Create(); checkForGlError("DrawCursorObj : m_rbo.Create"); m_rbo.Bind(); checkForGlError("DrawCursorObj : m_rbo.Bind"); m_rbo.Storage(GL_RGBA, m_width, m_height); checkForGlError("DrawCursorObj : m_rbo.Storage"); m_fbo.Renderbuffer(GL_COLOR_ATTACHMENT0, m_rbo.GetId()); checkForGlError("DrawCursorObj : m_fbo.Renderbuffer"); } m_fbo.Bind(); checkForGlError("DrawCursorObj : m_fbo.Bind"); glDrawBuffer(GL_COLOR_ATTACHMENT0); checkForGlError("DrawCursorObj : glDrawBuffer"); m_program.Use(); checkForGlError("DrawCursorObj : m_program.Use"); if (m_update_texture) { glUniform2f(m_program.GetLocation("in_tc"), m_width, m_height); checkForGlError("DrawCursorObj : glUniform2f"); if (!m_tex_id) { glGenTextures(1, &m_tex_id); checkForGlError("DrawCursorObj : glGenTextures"); } glActiveTexture(GL_TEXTURE0); checkForGlError("DrawCursorObj : glActiveTexture"); glBindTexture(GL_TEXTURE_2D, m_tex_id); checkForGlError("DrawCursorObj : glBindTexture"); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_width, m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, m_pixels); checkForGlError("DrawCursorObj : glTexImage2D"); m_program.SetTex(0); } if (m_update_pos) { glUniform4f(m_program.GetLocation("in_pos"), m_pos_x, m_pos_y, m_pos_z, 1.0f); checkForGlError("DrawCursorObj : glUniform4f"); } glDrawArrays(GL_QUADS, 0, 4); checkForGlError("DrawCursorObj : glDrawArrays"); m_fbo.Bind(GL_READ_FRAMEBUFFER); checkForGlError("DrawCursorObj : m_fbo.Bind(GL_READ_FRAMEBUFFER)"); GLfbo::Bind(GL_DRAW_FRAMEBUFFER, 0); checkForGlError("DrawCursorObj : GLfbo::Bind(GL_DRAW_FRAMEBUFFER, 0)"); GLfbo::Blit(0, 0, m_width, m_height, 0, 0, m_width, m_height, GL_COLOR_BUFFER_BIT, GL_NEAREST); checkForGlError("DrawCursorObj : GLfbo::Blit"); m_fbo.Bind(); checkForGlError("DrawCursorObj : m_fbo.Bind"); } void DrawCursorObj::InitializeShaders() { m_vp.shader = "#version 330\n" "\n" "uniform vec4 in_pos;\n" "uniform vec2 in_tc;\n" "out vec2 tc;\n" "\n" "void main()\n" "{\n" " tc = in_tc;\n" " gl_Position = in_pos;\n" "}\n"; m_fp.SetShaderText( "#version 330\n" "\n" "in vec2 tc;\n" "uniform sampler2D tex0;\n" "layout (location = 0) out vec4 res;\n" "\n" "void main()\n" "{\n" " res = texture(tex0, tc);\n" "}\n"); } void DrawCursorObj::SetTexture(void* pixels, int width, int height) { m_pixels = pixels; m_width = width; m_height = height; m_update_texture = true; } void DrawCursorObj::SetPosition(float x, float y, float z) { m_pos_x = x; m_pos_y = y; m_pos_z = z; m_update_pos = true; } void DrawCursorObj::InitializeLocations() { //LOG_WARNING(RSX, "tex0 location = 0x%x", m_program.GetLocation("tex0")); } GLGSRender::GLGSRender() : GSRender() , m_frame(nullptr) , m_fp_buf_num(-1) , m_vp_buf_num(-1) , m_context(nullptr) { m_frame = GetGSFrame(); } GLGSRender::~GLGSRender() { m_frame->Close(); m_frame->DeleteContext(m_context); } 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) { if (0) { u32 data_format = methodRegisters[NV4097_SET_VERTEX_DATA_ARRAY_FORMAT + i * 4]; u16 frequency = data_format >> 16; u8 stride = (data_format >> 8) & 0xff; u8 size = (data_format >> 4) & 0xf; u8 type = data_format & 0xf; u32 type_size = 1; switch (type) { case CELL_GCM_VERTEX_S1: type_size = 2; break; case CELL_GCM_VERTEX_F: type_size = 4; break; case CELL_GCM_VERTEX_SF: type_size = 2; break; case CELL_GCM_VERTEX_UB: type_size = 1; break; case CELL_GCM_VERTEX_S32K: type_size = 2; break; case CELL_GCM_VERTEX_CMP: type_size = 4; break; case CELL_GCM_VERTEX_UB256: type_size = 1; break; default: LOG_ERROR(RSX, "RSXVertexData::GetTypeSize: Bad vertex data type (%d)!", type); break; } int item_size = size * type_size; } offset_list[i] = cur_offset; if (!m_vertex_data[i].IsEnabled()) 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.data(), 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.data(), 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: LOG_ERROR(HLE, "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_FLOAT, // Needs conversion GL_UNSIGNED_BYTE, }; static const bool gl_normalized[] = { GL_TRUE, GL_FALSE, GL_FALSE, GL_TRUE, GL_FALSE, GL_TRUE, GL_FALSE, }; if (m_vertex_data[i].type < 1 || m_vertex_data[i].type > 7) { LOG_ERROR(RSX, "GLGSRender::EnableVertexData: Bad vertex data type (%d)!", m_vertex_data[i].type); } 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, reinterpret_cast(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()) continue; glDisableVertexAttribArray(i); checkForGlError("glDisableVertexAttribArray"); } m_vao.Unbind(); } void GLGSRender::InitVertexData() { int l; 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 }; for (const RSXTransformConstant& c : m_transform_constants) { const std::string name = fmt::Format("vc[%u]", c.id); l = m_program.GetLocation(name); checkForGlError("glGetUniformLocation " + name); 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 / RSXThread::m_width_scale); scaleOffsetMat[5] = (GLfloat&)methodRegisters[NV4097_SET_VIEWPORT_SCALE + (0x4 * 1)] / (RSXThread::m_height / RSXThread::m_height_scale); scaleOffsetMat[10] = (GLfloat&)methodRegisters[NV4097_SET_VIEWPORT_SCALE + (0x4 * 2)]; // Offset scaleOffsetMat[3] = (GLfloat&)methodRegisters[NV4097_SET_VIEWPORT_OFFSET + (0x4 * 0)] - (RSXThread::m_width / RSXThread::m_width_scale); scaleOffsetMat[7] = (GLfloat&)methodRegisters[NV4097_SET_VIEWPORT_OFFSET + (0x4 * 1)] - (RSXThread::m_height / RSXThread::m_height_scale); scaleOffsetMat[11] = (GLfloat&)methodRegisters[NV4097_SET_VIEWPORT_OFFSET + (0x4 * 2)] - 1 / 2.0f; scaleOffsetMat[3] /= RSXThread::m_width / RSXThread::m_width_scale; scaleOffsetMat[7] /= RSXThread::m_height / RSXThread::m_height_scale; l = m_program.GetLocation("scaleOffsetMat"); glUniformMatrix4fv(l, 1, false, scaleOffsetMat); checkForGlError("glUniformMatrix4fv"); } void GLGSRender::InitFragmentData() { if (!m_cur_shader_prog) { LOG_ERROR(RSX, "InitFragmentData: m_cur_shader_prog == NULL"); return; } for (const RSXTransformConstant& c : m_fragment_constants) { u32 id = c.id - m_cur_shader_prog->offset; //LOG_WARNING(RSX,"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()) // LOG_NOTICE(HLE, ""); } bool GLGSRender::LoadProgram() { if (!m_cur_shader_prog) { LOG_WARNING(RSX, "LoadProgram: m_cur_shader_prog == NULL"); return false; } m_cur_shader_prog->ctrl = m_shader_ctrl; if (!m_cur_vertex_prog) { LOG_WARNING(RSX, "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); if (m_fp_buf_num == -1) { LOG_WARNING(RSX, "FP not found in buffer!"); m_shader_prog.Decompile(*m_cur_shader_prog); m_shader_prog.Compile(); checkForGlError("m_shader_prog.Compile"); // TODO: This shouldn't use current dir rFile f("./FragmentProgram.txt", rFile::write); f.Write(m_shader_prog.GetShaderText()); } if (m_vp_buf_num == -1) { LOG_WARNING(RSX, "VP not found in buffer!"); m_vertex_prog.Decompile(*m_cur_vertex_prog); m_vertex_prog.Compile(); checkForGlError("m_vertex_prog.Compile"); // TODO: This shouldn't use current dir rFile f("./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::WriteBuffers() { if (Ini.GSDumpDepthBuffer.GetValue()) { glBindBuffer(GL_PIXEL_PACK_BUFFER, g_pbo[4]); glBufferData(GL_PIXEL_PACK_BUFFER, RSXThread::m_width * RSXThread::m_height * 4, 0, GL_STREAM_READ); glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); WriteDepthBuffer(); } if (Ini.GSDumpColorBuffers.GetValue()) { WriteColorBuffers(); } } 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)) { LOG_WARNING(RSX, "Bad depth buffer address: address=0x%x, offset=0x%x, dma=0x%x", address, m_surface_offset_z, m_context_dma_z); return; } auto ptr = vm::get_ptr(address); glBindBuffer(GL_PIXEL_PACK_BUFFER, g_pbo[4]); checkForGlError("WriteDepthBuffer(): glBindBuffer"); glReadPixels(0, 0, RSXThread::m_width, RSXThread::m_height, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, 0); checkForGlError("WriteDepthBuffer(): glReadPixels"); GLubyte *packed = (GLubyte *)glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY); if (packed) { memcpy(ptr, packed, RSXThread::m_width * RSXThread::m_height * 4); glUnmapBuffer(GL_PIXEL_PACK_BUFFER); checkForGlError("WriteDepthBuffer(): glUnmapBuffer"); } glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); checkForGlError("WriteDepthBuffer(): 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, ptr); checkForGlError("WriteDepthBuffer(): glTexImage2D"); glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, ptr); checkForGlError("WriteDepthBuffer(): glGetTexImage"); } void GLGSRender::WriteColorBufferA() { 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)) { LOG_ERROR(RSX, "Bad color 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("WriteColorBufferA(): glReadBuffer"); glBindBuffer(GL_PIXEL_PACK_BUFFER, g_pbo[0]); checkForGlError("WriteColorBufferA(): glBindBuffer"); glReadPixels(0, 0, RSXThread::m_width, RSXThread::m_height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8, 0); checkForGlError("WriteColorBufferA(): glReadPixels"); GLubyte *packed = (GLubyte *)glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY); if (packed) { memcpy(vm::get_ptr(address), packed, RSXThread::m_width * RSXThread::m_height * 4); glUnmapBuffer(GL_PIXEL_PACK_BUFFER); checkForGlError("WriteColorBufferA(): glUnmapBuffer"); } glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); } void GLGSRender::WriteColorBufferB() { 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)) { LOG_ERROR(RSX, "Bad color 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("WriteColorBufferB(): glReadBuffer"); glBindBuffer(GL_PIXEL_PACK_BUFFER, g_pbo[1]); checkForGlError("WriteColorBufferB(): glBindBuffer"); glReadPixels(0, 0, RSXThread::m_width, RSXThread::m_height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8, 0); checkForGlError("WriteColorBufferB(): glReadPixels"); GLubyte *packed = (GLubyte *)glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY); if (packed) { memcpy(vm::get_ptr(address), packed, RSXThread::m_width * RSXThread::m_height * 4); glUnmapBuffer(GL_PIXEL_PACK_BUFFER); checkForGlError("WriteColorBufferB(): glUnmapBuffer"); } glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); } void GLGSRender::WriteColorBufferC() { 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)) { LOG_ERROR(RSX, "Bad color 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("WriteColorBufferC(): glReadBuffer"); glBindBuffer(GL_PIXEL_PACK_BUFFER, g_pbo[2]); checkForGlError("WriteColorBufferC(): glBindBuffer"); glReadPixels(0, 0, RSXThread::m_width, RSXThread::m_height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8, 0); checkForGlError("WriteColorBufferC(): glReadPixels"); GLubyte *packed = (GLubyte *)glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY); if (packed) { memcpy(vm::get_ptr(address), packed, RSXThread::m_width * RSXThread::m_height * 4); glUnmapBuffer(GL_PIXEL_PACK_BUFFER); checkForGlError("WriteColorBufferC(): glUnmapBuffer"); } glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); } void GLGSRender::WriteColorBufferD() { 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)) { LOG_ERROR(RSX, "Bad color 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("WriteColorBufferD(): glReadBuffer"); glBindBuffer(GL_PIXEL_PACK_BUFFER, g_pbo[3]); checkForGlError("WriteColorBufferD(): glBindBuffer"); glReadPixels(0, 0, RSXThread::m_width, RSXThread::m_height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8, 0); checkForGlError("WriteColorBufferD(): glReadPixels"); GLubyte *packed = (GLubyte *)glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY); if (packed) { memcpy(vm::get_ptr(address), packed, RSXThread::m_width * RSXThread::m_height * 4); glUnmapBuffer(GL_PIXEL_PACK_BUFFER); checkForGlError("WriteColorBufferD(): glUnmapBuffer"); } glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); } void GLGSRender::WriteColorBuffers() { glPixelStorei(GL_PACK_ROW_LENGTH, 0); glPixelStorei(GL_PACK_ALIGNMENT, 4); switch(m_surface_color_target) { case CELL_GCM_SURFACE_TARGET_NONE: return; case CELL_GCM_SURFACE_TARGET_0: glBindBuffer(GL_PIXEL_PACK_BUFFER, g_pbo[0]); glBufferData(GL_PIXEL_PACK_BUFFER, RSXThread::m_width * RSXThread::m_height * 4, 0, GL_STREAM_READ); glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); WriteColorBufferA(); break; case CELL_GCM_SURFACE_TARGET_1: glBindBuffer(GL_PIXEL_PACK_BUFFER, g_pbo[1]); glBufferData(GL_PIXEL_PACK_BUFFER, RSXThread::m_width * RSXThread::m_height * 4, 0, GL_STREAM_READ); glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); WriteColorBufferB(); break; case CELL_GCM_SURFACE_TARGET_MRT1: for (int i = 0; i < 2; i++) { glBindBuffer(GL_PIXEL_PACK_BUFFER, g_pbo[i]); glBufferData(GL_PIXEL_PACK_BUFFER, RSXThread::m_width * RSXThread::m_height * 4, 0, GL_STREAM_READ); } glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); WriteColorBufferA(); WriteColorBufferB(); break; case CELL_GCM_SURFACE_TARGET_MRT2: for (int i = 0; i < 3; i++) { glBindBuffer(GL_PIXEL_PACK_BUFFER, g_pbo[i]); glBufferData(GL_PIXEL_PACK_BUFFER, RSXThread::m_width * RSXThread::m_height * 4, 0, GL_STREAM_READ); } glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); WriteColorBufferA(); WriteColorBufferB(); WriteColorBufferC(); break; case CELL_GCM_SURFACE_TARGET_MRT3: for (int i = 0; i < 4; i++) { glBindBuffer(GL_PIXEL_PACK_BUFFER, g_pbo[i]); glBufferData(GL_PIXEL_PACK_BUFFER, RSXThread::m_width * RSXThread::m_height * 4, 0, GL_STREAM_READ); } glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); WriteColorBufferA(); WriteColorBufferB(); WriteColorBufferC(); WriteColorBufferD(); break; } } void GLGSRender::OnInit() { m_draw_frames = 1; m_skip_frames = 0; RSXThread::m_width = 720; RSXThread::m_height = 576; RSXThread::m_width_scale = 2.0f; RSXThread::m_height_scale = 2.0f; last_width = 0; last_height = 0; last_depth_format = 0; m_frame->Show(); } void GLGSRender::OnInitThread() { m_context = m_frame->GetNewContext(); m_frame->SetCurrent(m_context); InitProcTable(); glEnable(GL_TEXTURE_2D); glEnable(GL_VERTEX_PROGRAM_POINT_SIZE); glGenTextures(1, &g_depth_tex); glGenTextures(1, &g_flip_tex); glGenBuffers(6, g_pbo); // 4 for color buffers + 1 for depth buffer + 1 for flip() #ifdef _WIN32 glSwapInterval(Ini.GSVSyncEnable.GetValue() ? 1 : 0); #endif } void GLGSRender::OnExitThread() { glDeleteTextures(1, &g_flip_tex); glDeleteTextures(1, &g_depth_tex); glDeleteBuffers(6, g_pbo); glDisable(GL_TEXTURE_2D); glDisable(GL_VERTEX_PROGRAM_POINT_SIZE); 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(); if (m_vbo.IsCreated()) { m_vbo.UnBind(); m_vbo.Delete(); } m_vao.Delete(); } void GLGSRender::InitDrawBuffers() { if (!m_fbo.IsCreated() || RSXThread::m_width != last_width || RSXThread::m_height != last_height || last_depth_format != m_surface_depth_format) { LOG_WARNING(RSX, "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); if (m_surface_depth_format == CELL_GCM_SURFACE_Z16) { m_rbo.Storage(GL_DEPTH_COMPONENT16, RSXThread::m_width, RSXThread::m_height); checkForGlError("m_rbo.Storage(GL_DEPTH_COMPONENT16)"); m_fbo.Renderbuffer(GL_DEPTH_ATTACHMENT, m_rbo.GetId(4)); checkForGlError("m_fbo.Renderbuffer(GL_DEPTH_ATTACHMENT)"); } else if (m_surface_depth_format == CELL_GCM_SURFACE_Z24S8) { m_rbo.Storage(GL_DEPTH24_STENCIL8, RSXThread::m_width, RSXThread::m_height); checkForGlError("m_rbo.Storage(GL_DEPTH24_STENCIL8)"); m_fbo.Renderbuffer(GL_DEPTH_ATTACHMENT, m_rbo.GetId(4)); checkForGlError("m_fbo.Renderbuffer(GL_DEPTH_ATTACHMENT)"); m_fbo.Renderbuffer(GL_STENCIL_ATTACHMENT, m_rbo.GetId(4)); checkForGlError("m_fbo.Renderbuffer(GL_STENCIL_ATTACHMENT)"); } 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)); } } 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(); static const GLenum draw_buffers[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT3 }; switch (m_surface_color_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; checkForGlError("glDrawBuffers"); default: LOG_ERROR(RSX, "Bad surface color target: %d", m_surface_color_target); break; } if (m_read_buffer) { u32 format = GL_BGRA; CellGcmDisplayInfo* buffers = vm::get_ptr(m_gcm_buffers_addr); u32 addr = GetAddress(buffers[m_gcm_current_buffer].offset, CELL_GCM_LOCATION_LOCAL); if (Memory.IsGoodAddr(addr)) { u32 width = buffers[m_gcm_current_buffer].width; u32 height = buffers[m_gcm_current_buffer].height; glDrawPixels(width, height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8, vm::get_ptr(addr)); } } } void GLGSRender::Enable(u32 cmd, u32 enable) { switch (cmd) { case NV4097_SET_DITHER_ENABLE: enable ? glEnable(GL_DITHER) : glDisable(GL_DITHER); break; case NV4097_SET_ALPHA_TEST_ENABLE: enable ? glEnable(GL_ALPHA_TEST) : glDisable(GL_ALPHA_TEST); break; case NV4097_SET_STENCIL_TEST_ENABLE: enable ? glEnable(GL_STENCIL_TEST) : glDisable(GL_STENCIL_TEST); break; case NV4097_SET_DEPTH_TEST_ENABLE: enable ? glEnable(GL_DEPTH_TEST) : glDisable(GL_DEPTH_TEST); break; case NV4097_SET_CULL_FACE_ENABLE: enable ? glEnable(GL_CULL_FACE) : glDisable(GL_CULL_FACE); break; case NV4097_SET_BLEND_ENABLE: enable ? glEnable(GL_BLEND) : glDisable(GL_BLEND); break; case NV4097_SET_POLY_OFFSET_FILL_ENABLE: enable ? glEnable(GL_POLYGON_OFFSET_FILL) : glDisable(GL_POLYGON_OFFSET_FILL); break; case NV4097_SET_POLY_OFFSET_LINE_ENABLE: enable ? glEnable(GL_POLYGON_OFFSET_LINE) : glDisable(GL_POLYGON_OFFSET_LINE); break; case NV4097_SET_POLY_OFFSET_POINT_ENABLE: enable ? glEnable(GL_POLYGON_OFFSET_POINT) : glDisable(GL_POLYGON_OFFSET_POINT); break; case NV4097_SET_LOGIC_OP_ENABLE: enable ? glEnable(GL_LOGIC_OP) : glDisable(GL_LOGIC_OP); break; case NV4097_SET_SPECULAR_ENABLE: enable ? glEnable(GL_LIGHTING) : glDisable(GL_LIGHTING); break; case NV4097_SET_LINE_SMOOTH_ENABLE: enable ? glEnable(GL_LINE_SMOOTH) : glDisable(GL_LINE_SMOOTH); break; case NV4097_SET_POLY_SMOOTH_ENABLE: enable ? glEnable(GL_POLYGON_SMOOTH) : glDisable(GL_POLYGON_SMOOTH); break; case NV4097_SET_RESTART_INDEX_ENABLE: enable ? glEnable(GL_PRIMITIVE_RESTART) : glDisable(GL_PRIMITIVE_RESTART); break; case NV4097_SET_POINT_SPRITE_CONTROL: enable ? glEnable(GL_POINT_SPRITE) : glDisable(GL_POINT_SPRITE); break; case NV4097_SET_LINE_STIPPLE: enable ? glEnable(GL_LINE_STIPPLE) : glDisable(GL_LINE_STIPPLE); break; case NV4097_SET_POLYGON_STIPPLE: enable ? glEnable(GL_POLYGON_STIPPLE) : glDisable(GL_POLYGON_STIPPLE); break; case NV4097_SET_DEPTH_BOUNDS_TEST_ENABLE: enable ? glEnable(GL_DEPTH_BOUNDS_TEST_EXT) : glDisable(GL_DEPTH_BOUNDS_TEST_EXT); break; case NV4097_SET_USER_CLIP_PLANE_CONTROL: const u32 clip_plane_0 = enable & 0xf; const u32 clip_plane_1 = (enable >> 4) & 0xf; const u32 clip_plane_2 = (enable >> 8) & 0xf; const u32 clip_plane_3 = (enable >> 12) & 0xf; const u32 clip_plane_4 = (enable >> 16) & 0xf; const u32 clip_plane_5 = enable >> 20; clip_plane_0 ? glEnable(GL_CLIP_PLANE0) : glDisable(GL_CLIP_PLANE0); clip_plane_1 ? glEnable(GL_CLIP_PLANE1) : glDisable(GL_CLIP_PLANE1); clip_plane_2 ? glEnable(GL_CLIP_PLANE2) : glDisable(GL_CLIP_PLANE2); clip_plane_3 ? glEnable(GL_CLIP_PLANE3) : glDisable(GL_CLIP_PLANE3); clip_plane_4 ? glEnable(GL_CLIP_PLANE4) : glDisable(GL_CLIP_PLANE4); clip_plane_5 ? glEnable(GL_CLIP_PLANE5) : glDisable(GL_CLIP_PLANE5); break; } } void GLGSRender::ClearColor(u32 a, u32 r, u32 g, u32 b) { glClearColor(r / 255.0f, g / 255.0f, b / 255.0f, a / 255.0f); checkForGlError("glClearColor"); } void GLGSRender::ClearStencil(u32 stencil) { glClearStencil(stencil); checkForGlError("glClearStencil"); } void GLGSRender::ClearDepth(u32 depth) { glClearDepth(depth / (float)0xffffff); checkForGlError("glClearDepth"); } void GLGSRender::ClearSurface(u32 mask) { InitDrawBuffers(); GLbitfield clearMask = 0; if (mask & 0x01) clearMask |= GL_DEPTH_BUFFER_BIT; if (mask & 0x02) clearMask |= GL_STENCIL_BUFFER_BIT; if (mask & 0xF0) clearMask |= GL_COLOR_BUFFER_BIT; glClear(clearMask); checkForGlError("glClear"); WriteBuffers(); } void GLGSRender::ColorMask(bool a, bool r, bool g, bool b) { glColorMask(r, g, b, a); checkForGlError("glColorMask"); } void GLGSRender::AlphaFunc(u32 func, float ref) { glAlphaFunc(func, ref); checkForGlError("glAlphaFunc"); } void GLGSRender::DepthFunc(u32 func) { glDepthFunc(func); checkForGlError("glDepthFunc"); } void GLGSRender::DepthMask(u32 flag) { glDepthMask(flag); checkForGlError("glDepthMask"); } void GLGSRender::ExecCMD() { if (!LoadProgram()) { LOG_ERROR(RSX, "LoadProgram failed."); Emu.Pause(); return; } InitDrawBuffers(); 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_two_side_light_enable) { glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE); checkForGlError("glLightModeli"); } if (m_set_shade_mode) { glShadeModel(m_shade_mode); checkForGlError("glShadeModel"); } if (m_set_depth_bounds) { glDepthBoundsEXT(m_depth_bounds_min, m_depth_bounds_max); checkForGlError("glDepthBounds"); } if (m_set_clip) { glDepthRangef(m_clip_min, m_clip_max); checkForGlError("glDepthRangef"); } if (m_set_line_width) { glLineWidth(m_line_width); checkForGlError("glLineWidth"); } if (m_set_line_stipple) { glLineStipple(m_line_stipple_factor, m_line_stipple_pattern); checkForGlError("glLineStipple"); } if (m_set_polygon_stipple) { glPolygonStipple((const GLubyte*)m_polygon_stipple_pattern); checkForGlError("glPolygonStipple"); } 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) { glFrontFace(m_front_face); checkForGlError("glFrontFace"); } 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); checkForGlError("glPrimitiveRestartIndex"); } if (m_indexed_array.m_count && m_draw_array_count) { LOG_WARNING(RSX, "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)); } for (u32 i = 0; i < m_textures_count; ++i) { if (!m_vertex_textures[i].IsEnabled()) continue; glActiveTexture(GL_TEXTURE0 + m_textures_count + i); checkForGlError("glActiveTexture"); m_gl_vertex_textures[i].Create(); m_gl_vertex_textures[i].Bind(); checkForGlError(fmt::Format("m_gl_vertex_textures[%d].Bind", i)); m_program.SetTex(i); m_gl_vertex_textures[i].Init(m_vertex_textures[i]); checkForGlError(fmt::Format("m_gl_vertex_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); } if (m_indexed_array.m_count || m_draw_array_count) { 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: LOG_ERROR(RSX, "Bad indexed array type (%d)", m_indexed_array.m_type); break; } DisableVertexData(); m_indexed_array.Reset(); } if (m_draw_array_count) { //LOG_WARNING(RSX,"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(); } WriteBuffers(); } void GLGSRender::Flip() { 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; CellGcmDisplayInfo* buffers = vm::get_ptr(m_gcm_buffers_addr); u32 addr = GetAddress(buffers[m_gcm_current_buffer].offset, CELL_GCM_LOCATION_LOCAL); if (Memory.IsGoodAddr(addr)) { width = buffers[m_gcm_current_buffer].width; height = buffers[m_gcm_current_buffer].height; src_buffer = vm::get_ptr(addr); } else { src_buffer = nullptr; } } else if (m_fbo.IsCreated()) { format = GL_RGBA; static std::vector pixels; pixels.resize(RSXThread::m_width * RSXThread::m_height * 4); m_fbo.Bind(GL_READ_FRAMEBUFFER); glBindBuffer(GL_PIXEL_PACK_BUFFER, g_pbo[5]); glBufferData(GL_PIXEL_PACK_BUFFER, RSXThread::m_width * RSXThread::m_height * 4, 0, GL_STREAM_READ); glReadPixels(0, 0, RSXThread::m_width, RSXThread::m_height, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, 0); checkForGlError("Flip(): glReadPixels(GL_BGRA, GL_UNSIGNED_INT_8_8_8_8)"); GLubyte *packed = (GLubyte *)glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY); if (packed) { memcpy(pixels.data(), packed, RSXThread::m_width * RSXThread::m_height * 4); glUnmapBuffer(GL_PIXEL_PACK_BUFFER); checkForGlError("Flip(): glUnmapBuffer"); } glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); 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(); } // Draw Objects for (uint i = 0; i < m_post_draw_objs.size(); ++i) { m_post_draw_objs[i].Draw(); } m_frame->Flip(m_context); // Restore scissor if (m_set_scissor_horizontal && m_set_scissor_vertical) { glScissor(m_scissor_x, m_scissor_y, m_scissor_w, m_scissor_h); checkForGlError("glScissor"); } } 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; }