forked from dolphin-emu/dolphin
		
	
		
			
				
	
	
		
			1817 lines
		
	
	
		
			62 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			1817 lines
		
	
	
		
			62 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
// Copyright 2008 Dolphin Emulator Project
 | 
						|
// Licensed under GPLv2+
 | 
						|
// Refer to the license.txt file included.
 | 
						|
 | 
						|
#include <algorithm>
 | 
						|
#include <cinttypes>
 | 
						|
#include <cmath>
 | 
						|
#include <cstdio>
 | 
						|
#include <memory>
 | 
						|
#include <string>
 | 
						|
#include <vector>
 | 
						|
 | 
						|
#include "Common/Atomic.h"
 | 
						|
#include "Common/CommonTypes.h"
 | 
						|
#include "Common/FileUtil.h"
 | 
						|
#include "Common/GL/GLInterfaceBase.h"
 | 
						|
#include "Common/GL/GLUtil.h"
 | 
						|
#include "Common/Logging/LogManager.h"
 | 
						|
#include "Common/MathUtil.h"
 | 
						|
#include "Common/StringUtil.h"
 | 
						|
 | 
						|
#include "Core/Core.h"
 | 
						|
 | 
						|
#include "VideoBackends/OGL/BoundingBox.h"
 | 
						|
#include "VideoBackends/OGL/FramebufferManager.h"
 | 
						|
#include "VideoBackends/OGL/PostProcessing.h"
 | 
						|
#include "VideoBackends/OGL/ProgramShaderCache.h"
 | 
						|
#include "VideoBackends/OGL/RasterFont.h"
 | 
						|
#include "VideoBackends/OGL/Render.h"
 | 
						|
#include "VideoBackends/OGL/SamplerCache.h"
 | 
						|
#include "VideoBackends/OGL/TextureCache.h"
 | 
						|
#include "VideoBackends/OGL/VertexManager.h"
 | 
						|
 | 
						|
#include "VideoCommon/AVIDump.h"
 | 
						|
#include "VideoCommon/BPFunctions.h"
 | 
						|
#include "VideoCommon/DriverDetails.h"
 | 
						|
#include "VideoCommon/Fifo.h"
 | 
						|
#include "VideoCommon/IndexGenerator.h"
 | 
						|
#include "VideoCommon/OnScreenDisplay.h"
 | 
						|
#include "VideoCommon/PixelEngine.h"
 | 
						|
#include "VideoCommon/PixelShaderManager.h"
 | 
						|
#include "VideoCommon/RenderState.h"
 | 
						|
#include "VideoCommon/VertexShaderManager.h"
 | 
						|
#include "VideoCommon/VideoConfig.h"
 | 
						|
 | 
						|
void VideoConfig::UpdateProjectionHack()
 | 
						|
{
 | 
						|
  ::UpdateProjectionHack(g_Config.iPhackvalue, g_Config.sPhackvalue);
 | 
						|
}
 | 
						|
 | 
						|
static int s_max_texture_size = 0;
 | 
						|
 | 
						|
namespace OGL
 | 
						|
{
 | 
						|
VideoConfig g_ogl_config;
 | 
						|
 | 
						|
// Declarations and definitions
 | 
						|
// ----------------------------
 | 
						|
static std::unique_ptr<RasterFont> s_raster_font;
 | 
						|
 | 
						|
// 1 for no MSAA. Use s_MSAASamples > 1 to check for MSAA.
 | 
						|
static int s_MSAASamples = 1;
 | 
						|
static int s_last_multisamples = 1;
 | 
						|
static bool s_last_stereo_mode = false;
 | 
						|
static bool s_last_xfb_mode = false;
 | 
						|
 | 
						|
static bool s_vsync;
 | 
						|
 | 
						|
// EFB cache related
 | 
						|
static const u32 EFB_CACHE_RECT_SIZE = 64;  // Cache 64x64 blocks.
 | 
						|
static const u32 EFB_CACHE_WIDTH =
 | 
						|
    (EFB_WIDTH + EFB_CACHE_RECT_SIZE - 1) / EFB_CACHE_RECT_SIZE;  // round up
 | 
						|
static const u32 EFB_CACHE_HEIGHT = (EFB_HEIGHT + EFB_CACHE_RECT_SIZE - 1) / EFB_CACHE_RECT_SIZE;
 | 
						|
static bool s_efbCacheValid[2][EFB_CACHE_WIDTH * EFB_CACHE_HEIGHT];
 | 
						|
static bool s_efbCacheIsCleared = false;
 | 
						|
static std::vector<u32>
 | 
						|
    s_efbCache[2][EFB_CACHE_WIDTH * EFB_CACHE_HEIGHT];  // 2 for PEEK_Z and PEEK_COLOR
 | 
						|
 | 
						|
static void APIENTRY ErrorCallback(GLenum source, GLenum type, GLuint id, GLenum severity,
 | 
						|
                                   GLsizei length, const char* message, const void* userParam)
 | 
						|
{
 | 
						|
  const char* s_source;
 | 
						|
  const char* s_type;
 | 
						|
 | 
						|
  switch (source)
 | 
						|
  {
 | 
						|
  case GL_DEBUG_SOURCE_API_ARB:
 | 
						|
    s_source = "API";
 | 
						|
    break;
 | 
						|
  case GL_DEBUG_SOURCE_WINDOW_SYSTEM_ARB:
 | 
						|
    s_source = "Window System";
 | 
						|
    break;
 | 
						|
  case GL_DEBUG_SOURCE_SHADER_COMPILER_ARB:
 | 
						|
    s_source = "Shader Compiler";
 | 
						|
    break;
 | 
						|
  case GL_DEBUG_SOURCE_THIRD_PARTY_ARB:
 | 
						|
    s_source = "Third Party";
 | 
						|
    break;
 | 
						|
  case GL_DEBUG_SOURCE_APPLICATION_ARB:
 | 
						|
    s_source = "Application";
 | 
						|
    break;
 | 
						|
  case GL_DEBUG_SOURCE_OTHER_ARB:
 | 
						|
    s_source = "Other";
 | 
						|
    break;
 | 
						|
  default:
 | 
						|
    s_source = "Unknown";
 | 
						|
    break;
 | 
						|
  }
 | 
						|
  switch (type)
 | 
						|
  {
 | 
						|
  case GL_DEBUG_TYPE_ERROR_ARB:
 | 
						|
    s_type = "Error";
 | 
						|
    break;
 | 
						|
  case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB:
 | 
						|
    s_type = "Deprecated";
 | 
						|
    break;
 | 
						|
  case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB:
 | 
						|
    s_type = "Undefined";
 | 
						|
    break;
 | 
						|
  case GL_DEBUG_TYPE_PORTABILITY_ARB:
 | 
						|
    s_type = "Portability";
 | 
						|
    break;
 | 
						|
  case GL_DEBUG_TYPE_PERFORMANCE_ARB:
 | 
						|
    s_type = "Performance";
 | 
						|
    break;
 | 
						|
  case GL_DEBUG_TYPE_OTHER_ARB:
 | 
						|
    s_type = "Other";
 | 
						|
    break;
 | 
						|
  default:
 | 
						|
    s_type = "Unknown";
 | 
						|
    break;
 | 
						|
  }
 | 
						|
  switch (severity)
 | 
						|
  {
 | 
						|
  case GL_DEBUG_SEVERITY_HIGH_ARB:
 | 
						|
    ERROR_LOG(HOST_GPU, "id: %x, source: %s, type: %s - %s", id, s_source, s_type, message);
 | 
						|
    break;
 | 
						|
  case GL_DEBUG_SEVERITY_MEDIUM_ARB:
 | 
						|
    WARN_LOG(HOST_GPU, "id: %x, source: %s, type: %s - %s", id, s_source, s_type, message);
 | 
						|
    break;
 | 
						|
  case GL_DEBUG_SEVERITY_LOW_ARB:
 | 
						|
    DEBUG_LOG(HOST_GPU, "id: %x, source: %s, type: %s - %s", id, s_source, s_type, message);
 | 
						|
    break;
 | 
						|
  case GL_DEBUG_SEVERITY_NOTIFICATION:
 | 
						|
    DEBUG_LOG(HOST_GPU, "id: %x, source: %s, type: %s - %s", id, s_source, s_type, message);
 | 
						|
    break;
 | 
						|
  default:
 | 
						|
    ERROR_LOG(HOST_GPU, "id: %x, source: %s, type: %s - %s", id, s_source, s_type, message);
 | 
						|
    break;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
// Two small Fallbacks to avoid GL_ARB_ES2_compatibility
 | 
						|
static void APIENTRY DepthRangef(GLfloat neardepth, GLfloat fardepth)
 | 
						|
{
 | 
						|
  glDepthRange(neardepth, fardepth);
 | 
						|
}
 | 
						|
static void APIENTRY ClearDepthf(GLfloat depthval)
 | 
						|
{
 | 
						|
  glClearDepth(depthval);
 | 
						|
}
 | 
						|
 | 
						|
static void InitDriverInfo()
 | 
						|
{
 | 
						|
  std::string svendor = std::string(g_ogl_config.gl_vendor);
 | 
						|
  std::string srenderer = std::string(g_ogl_config.gl_renderer);
 | 
						|
  std::string sversion = std::string(g_ogl_config.gl_version);
 | 
						|
  DriverDetails::Vendor vendor = DriverDetails::VENDOR_UNKNOWN;
 | 
						|
  DriverDetails::Driver driver = DriverDetails::DRIVER_UNKNOWN;
 | 
						|
  DriverDetails::Family family = DriverDetails::Family::UNKNOWN;
 | 
						|
  double version = 0.0;
 | 
						|
 | 
						|
  // Get the vendor first
 | 
						|
  if (svendor == "NVIDIA Corporation" && srenderer != "NVIDIA Tegra")
 | 
						|
  {
 | 
						|
    vendor = DriverDetails::VENDOR_NVIDIA;
 | 
						|
  }
 | 
						|
  else if (svendor == "ATI Technologies Inc." || svendor == "Advanced Micro Devices, Inc.")
 | 
						|
  {
 | 
						|
    vendor = DriverDetails::VENDOR_ATI;
 | 
						|
  }
 | 
						|
  else if (std::string::npos != sversion.find("Mesa"))
 | 
						|
  {
 | 
						|
    vendor = DriverDetails::VENDOR_MESA;
 | 
						|
  }
 | 
						|
  else if (std::string::npos != svendor.find("Intel"))
 | 
						|
  {
 | 
						|
    vendor = DriverDetails::VENDOR_INTEL;
 | 
						|
  }
 | 
						|
  else if (svendor == "ARM")
 | 
						|
  {
 | 
						|
    vendor = DriverDetails::VENDOR_ARM;
 | 
						|
  }
 | 
						|
  else if (svendor == "http://limadriver.org/")
 | 
						|
  {
 | 
						|
    vendor = DriverDetails::VENDOR_ARM;
 | 
						|
    driver = DriverDetails::DRIVER_LIMA;
 | 
						|
  }
 | 
						|
  else if (svendor == "Qualcomm")
 | 
						|
  {
 | 
						|
    vendor = DriverDetails::VENDOR_QUALCOMM;
 | 
						|
  }
 | 
						|
  else if (svendor == "Imagination Technologies")
 | 
						|
  {
 | 
						|
    vendor = DriverDetails::VENDOR_IMGTEC;
 | 
						|
  }
 | 
						|
  else if (svendor == "NVIDIA Corporation" && srenderer == "NVIDIA Tegra")
 | 
						|
  {
 | 
						|
    vendor = DriverDetails::VENDOR_TEGRA;
 | 
						|
  }
 | 
						|
  else if (svendor == "Vivante Corporation")
 | 
						|
  {
 | 
						|
    vendor = DriverDetails::VENDOR_VIVANTE;
 | 
						|
  }
 | 
						|
 | 
						|
  // Get device family and driver version...if we care about it
 | 
						|
  switch (vendor)
 | 
						|
  {
 | 
						|
  case DriverDetails::VENDOR_QUALCOMM:
 | 
						|
  {
 | 
						|
    driver = DriverDetails::DRIVER_QUALCOMM;
 | 
						|
    double glVersion;
 | 
						|
    sscanf(g_ogl_config.gl_version, "OpenGL ES %lg V@%lg", &glVersion, &version);
 | 
						|
  }
 | 
						|
  break;
 | 
						|
  case DriverDetails::VENDOR_ARM:
 | 
						|
    // Currently the Mali-T line has two families in it.
 | 
						|
    // Mali-T6xx and Mali-T7xx
 | 
						|
    // These two families are similar enough that they share bugs in their drivers.
 | 
						|
    //
 | 
						|
    // Mali drivers provide no way to explicitly find out what video driver is running.
 | 
						|
    // This is similar to how we can't find the Nvidia driver version in Windows.
 | 
						|
    // Good thing is that ARM introduces a new video driver about once every two years so we can
 | 
						|
    // find the driver version by the features it exposes.
 | 
						|
    // r2p0 - No OpenGL ES 3.0 support (We don't support this)
 | 
						|
    // r3p0 - OpenGL ES 3.0 support
 | 
						|
    // r4p0 - Supports 'GL_EXT_shader_pixel_local_storage' extension.
 | 
						|
 | 
						|
    driver = DriverDetails::DRIVER_ARM;
 | 
						|
    if (GLExtensions::Supports("GL_EXT_shader_pixel_local_storage"))
 | 
						|
      version = 400;
 | 
						|
    else
 | 
						|
      version = 300;
 | 
						|
    break;
 | 
						|
  case DriverDetails::VENDOR_MESA:
 | 
						|
  {
 | 
						|
    if (svendor == "nouveau")
 | 
						|
    {
 | 
						|
      driver = DriverDetails::DRIVER_NOUVEAU;
 | 
						|
    }
 | 
						|
    else if (svendor == "Intel Open Source Technology Center")
 | 
						|
    {
 | 
						|
      driver = DriverDetails::DRIVER_I965;
 | 
						|
      if (srenderer.find("Sandybridge") != std::string::npos)
 | 
						|
        family = DriverDetails::Family::INTEL_SANDY;
 | 
						|
      else if (srenderer.find("Ivybridge") != std::string::npos)
 | 
						|
        family = DriverDetails::Family::INTEL_IVY;
 | 
						|
    }
 | 
						|
    else if (std::string::npos != srenderer.find("AMD") ||
 | 
						|
             std::string::npos != srenderer.find("ATI"))
 | 
						|
    {
 | 
						|
      driver = DriverDetails::DRIVER_R600;
 | 
						|
    }
 | 
						|
 | 
						|
    int major = 0;
 | 
						|
    int minor = 0;
 | 
						|
    int release = 0;
 | 
						|
    sscanf(g_ogl_config.gl_version, "%*s (Core Profile) Mesa %d.%d.%d", &major, &minor, &release);
 | 
						|
    version = 100 * major + 10 * minor + release;
 | 
						|
  }
 | 
						|
  break;
 | 
						|
  case DriverDetails::VENDOR_INTEL:  // Happens in OS X/Windows
 | 
						|
  {
 | 
						|
    u32 market_name;
 | 
						|
    sscanf(g_ogl_config.gl_renderer, "Intel HD Graphics %d", &market_name);
 | 
						|
    switch (market_name)
 | 
						|
    {
 | 
						|
    case 2000:
 | 
						|
    case 3000:
 | 
						|
      family = DriverDetails::Family::INTEL_SANDY;
 | 
						|
      break;
 | 
						|
    case 2500:
 | 
						|
    case 4000:
 | 
						|
      family = DriverDetails::Family::INTEL_IVY;
 | 
						|
      break;
 | 
						|
    default:
 | 
						|
      family = DriverDetails::Family::UNKNOWN;
 | 
						|
      break;
 | 
						|
    };
 | 
						|
#ifdef _WIN32
 | 
						|
    int glmajor = 0;
 | 
						|
    int glminor = 0;
 | 
						|
    int major = 0;
 | 
						|
    int minor = 0;
 | 
						|
    int release = 0;
 | 
						|
    int revision = 0;
 | 
						|
    // Example version string: '4.3.0 - Build 10.18.10.3907'
 | 
						|
    sscanf(g_ogl_config.gl_version, "%d.%d.0 - Build %d.%d.%d.%d", &glmajor, &glminor, &major,
 | 
						|
           &minor, &release, &revision);
 | 
						|
    version = 100000000 * major + 1000000 * minor + 10000 * release + revision;
 | 
						|
    version /= 10000;
 | 
						|
#endif
 | 
						|
  }
 | 
						|
  break;
 | 
						|
  case DriverDetails::VENDOR_NVIDIA:
 | 
						|
  {
 | 
						|
    int glmajor = 0;
 | 
						|
    int glminor = 0;
 | 
						|
    int glrelease = 0;
 | 
						|
    int major = 0;
 | 
						|
    int minor = 0;
 | 
						|
    // TODO: this is known to be broken on Windows
 | 
						|
    // Nvidia seems to have removed their driver version from this string, so we can't get it.
 | 
						|
    // hopefully we'll never have to workaround Nvidia bugs
 | 
						|
    sscanf(g_ogl_config.gl_version, "%d.%d.%d NVIDIA %d.%d", &glmajor, &glminor, &glrelease, &major,
 | 
						|
           &minor);
 | 
						|
    version = 100 * major + minor;
 | 
						|
  }
 | 
						|
  break;
 | 
						|
  // We don't care about these
 | 
						|
  default:
 | 
						|
    break;
 | 
						|
  }
 | 
						|
  DriverDetails::Init(DriverDetails::API_OPENGL, vendor, driver, version, family);
 | 
						|
}
 | 
						|
 | 
						|
// Init functions
 | 
						|
Renderer::Renderer()
 | 
						|
{
 | 
						|
  bool bSuccess = true;
 | 
						|
 | 
						|
  // Init extension support.
 | 
						|
  if (!GLExtensions::Init())
 | 
						|
  {
 | 
						|
    // OpenGL 2.0 is required for all shader based drawings. There is no way to get this by
 | 
						|
    // extensions
 | 
						|
    PanicAlert("GPU: OGL ERROR: Does your video card support OpenGL 2.0?");
 | 
						|
    bSuccess = false;
 | 
						|
  }
 | 
						|
 | 
						|
  g_ogl_config.gl_vendor = (const char*)glGetString(GL_VENDOR);
 | 
						|
  g_ogl_config.gl_renderer = (const char*)glGetString(GL_RENDERER);
 | 
						|
  g_ogl_config.gl_version = (const char*)glGetString(GL_VERSION);
 | 
						|
 | 
						|
  InitDriverInfo();
 | 
						|
 | 
						|
  if (GLExtensions::Version() < 300)
 | 
						|
  {
 | 
						|
    // integer vertex attributes require a gl3 only function
 | 
						|
    PanicAlert("GPU: OGL ERROR: Need OpenGL version 3.\n"
 | 
						|
               "GPU: Does your video card support OpenGL 3?");
 | 
						|
    bSuccess = false;
 | 
						|
  }
 | 
						|
 | 
						|
  // check for the max vertex attributes
 | 
						|
  GLint numvertexattribs = 0;
 | 
						|
  glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &numvertexattribs);
 | 
						|
  if (numvertexattribs < 16)
 | 
						|
  {
 | 
						|
    PanicAlert("GPU: OGL ERROR: Number of attributes %d not enough.\n"
 | 
						|
               "GPU: Does your video card support OpenGL 2.x?",
 | 
						|
               numvertexattribs);
 | 
						|
    bSuccess = false;
 | 
						|
  }
 | 
						|
 | 
						|
  // check the max texture width and height
 | 
						|
  GLint max_texture_size;
 | 
						|
  glGetIntegerv(GL_MAX_TEXTURE_SIZE, (GLint*)&max_texture_size);
 | 
						|
  if (max_texture_size < 1024)
 | 
						|
  {
 | 
						|
    PanicAlert("GL_MAX_TEXTURE_SIZE too small at %i - must be at least 1024.", max_texture_size);
 | 
						|
    bSuccess = false;
 | 
						|
  }
 | 
						|
 | 
						|
  if (GLInterface->GetMode() == GLInterfaceMode::MODE_OPENGL)
 | 
						|
  {
 | 
						|
    if (!GLExtensions::Supports("GL_ARB_framebuffer_object"))
 | 
						|
    {
 | 
						|
      // We want the ogl3 framebuffer instead of the ogl2 one for better blitting support.
 | 
						|
      // It's also compatible with the gles3 one.
 | 
						|
      PanicAlert("GPU: ERROR: Need GL_ARB_framebuffer_object for multiple render targets.\n"
 | 
						|
                 "GPU: Does your video card support OpenGL 3.0?");
 | 
						|
      bSuccess = false;
 | 
						|
    }
 | 
						|
 | 
						|
    if (!GLExtensions::Supports("GL_ARB_vertex_array_object"))
 | 
						|
    {
 | 
						|
      // This extension is used to replace lots of pointer setting function.
 | 
						|
      // Also gles3 requires to use it.
 | 
						|
      PanicAlert("GPU: OGL ERROR: Need GL_ARB_vertex_array_object.\n"
 | 
						|
                 "GPU: Does your video card support OpenGL 3.0?");
 | 
						|
      bSuccess = false;
 | 
						|
    }
 | 
						|
 | 
						|
    if (!GLExtensions::Supports("GL_ARB_map_buffer_range"))
 | 
						|
    {
 | 
						|
      // ogl3 buffer mapping for better streaming support.
 | 
						|
      // The ogl2 one also isn't in gles3.
 | 
						|
      PanicAlert("GPU: OGL ERROR: Need GL_ARB_map_buffer_range.\n"
 | 
						|
                 "GPU: Does your video card support OpenGL 3.0?");
 | 
						|
      bSuccess = false;
 | 
						|
    }
 | 
						|
 | 
						|
    if (!GLExtensions::Supports("GL_ARB_uniform_buffer_object"))
 | 
						|
    {
 | 
						|
      // ubo allow us to keep the current constants on shader switches
 | 
						|
      // we also can stream them much nicer and pack into it whatever we want to
 | 
						|
      PanicAlert("GPU: OGL ERROR: Need GL_ARB_uniform_buffer_object.\n"
 | 
						|
                 "GPU: Does your video card support OpenGL 3.1?");
 | 
						|
      bSuccess = false;
 | 
						|
    }
 | 
						|
    else if (DriverDetails::HasBug(DriverDetails::BUG_BROKEN_UBO))
 | 
						|
    {
 | 
						|
      PanicAlert(
 | 
						|
          "Buggy GPU driver detected.\n"
 | 
						|
          "Please either install the closed-source GPU driver or update your Mesa 3D version.");
 | 
						|
      bSuccess = false;
 | 
						|
    }
 | 
						|
 | 
						|
    if (!GLExtensions::Supports("GL_ARB_sampler_objects"))
 | 
						|
    {
 | 
						|
      // Our sampler cache uses this extension. It could easyly be workaround and it's by far the
 | 
						|
      // highest requirement, but it seems that no driver lacks support for it.
 | 
						|
      PanicAlert("GPU: OGL ERROR: Need GL_ARB_sampler_objects.\n"
 | 
						|
                 "GPU: Does your video card support OpenGL 3.3?");
 | 
						|
      bSuccess = false;
 | 
						|
    }
 | 
						|
 | 
						|
    // OpenGL 3 doesn't provide GLES like float functions for depth.
 | 
						|
    // They are in core in OpenGL 4.1, so almost every driver should support them.
 | 
						|
    // But for the oldest ones, we provide fallbacks to the old double functions.
 | 
						|
    if (!GLExtensions::Supports("GL_ARB_ES2_compatibility"))
 | 
						|
    {
 | 
						|
      glDepthRangef = DepthRangef;
 | 
						|
      glClearDepthf = ClearDepthf;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  // Copy the GPU name to g_Config, so Analytics can see it.
 | 
						|
  g_Config.backend_info.AdapterName = g_ogl_config.gl_renderer;
 | 
						|
 | 
						|
  g_Config.backend_info.bSupportsDualSourceBlend =
 | 
						|
      (GLExtensions::Supports("GL_ARB_blend_func_extended") ||
 | 
						|
       GLExtensions::Supports("GL_EXT_blend_func_extended"));
 | 
						|
  g_Config.backend_info.bSupportsPrimitiveRestart =
 | 
						|
      !DriverDetails::HasBug(DriverDetails::BUG_PRIMITIVE_RESTART) &&
 | 
						|
      ((GLExtensions::Version() >= 310) || GLExtensions::Supports("GL_NV_primitive_restart"));
 | 
						|
  g_Config.backend_info.bSupportsBBox =
 | 
						|
      GLExtensions::Supports("GL_ARB_shader_storage_buffer_object");
 | 
						|
  g_Config.backend_info.bSupportsGSInstancing = GLExtensions::Supports("GL_ARB_gpu_shader5");
 | 
						|
  g_Config.backend_info.bSupportsSSAA = GLExtensions::Supports("GL_ARB_gpu_shader5") &&
 | 
						|
                                        GLExtensions::Supports("GL_ARB_sample_shading");
 | 
						|
  g_Config.backend_info.bSupportsGeometryShaders =
 | 
						|
      GLExtensions::Version() >= 320 &&
 | 
						|
      !DriverDetails::HasBug(DriverDetails::BUG_BROKEN_GEOMETRY_SHADERS);
 | 
						|
  g_Config.backend_info.bSupportsPaletteConversion =
 | 
						|
      GLExtensions::Supports("GL_ARB_texture_buffer_object") ||
 | 
						|
      GLExtensions::Supports("GL_OES_texture_buffer") ||
 | 
						|
      GLExtensions::Supports("GL_EXT_texture_buffer");
 | 
						|
  g_Config.backend_info.bSupportsClipControl = GLExtensions::Supports("GL_ARB_clip_control");
 | 
						|
  g_ogl_config.bSupportsCopySubImage =
 | 
						|
      (GLExtensions::Supports("GL_ARB_copy_image") || GLExtensions::Supports("GL_NV_copy_image") ||
 | 
						|
       GLExtensions::Supports("GL_EXT_copy_image") ||
 | 
						|
       GLExtensions::Supports("GL_OES_copy_image")) &&
 | 
						|
      !DriverDetails::HasBug(DriverDetails::BUG_BROKEN_COPYIMAGE);
 | 
						|
 | 
						|
  // Desktop OpenGL supports the binding layout if it supports 420pack
 | 
						|
  // OpenGL ES 3.1 supports it implicitly without an extension
 | 
						|
  g_Config.backend_info.bSupportsBindingLayout =
 | 
						|
      GLExtensions::Supports("GL_ARB_shading_language_420pack");
 | 
						|
 | 
						|
  // Clip distance support is useless without a method to clamp the depth range
 | 
						|
  g_Config.backend_info.bSupportsDepthClamp = GLExtensions::Supports("GL_ARB_depth_clamp");
 | 
						|
 | 
						|
  g_ogl_config.bSupportsGLSLCache = GLExtensions::Supports("GL_ARB_get_program_binary");
 | 
						|
  g_ogl_config.bSupportsGLPinnedMemory = GLExtensions::Supports("GL_AMD_pinned_memory");
 | 
						|
  g_ogl_config.bSupportsGLSync = GLExtensions::Supports("GL_ARB_sync");
 | 
						|
  g_ogl_config.bSupportsGLBaseVertex = GLExtensions::Supports("GL_ARB_draw_elements_base_vertex") ||
 | 
						|
                                       GLExtensions::Supports("GL_EXT_draw_elements_base_vertex") ||
 | 
						|
                                       GLExtensions::Supports("GL_OES_draw_elements_base_vertex");
 | 
						|
  g_ogl_config.bSupportsGLBufferStorage = GLExtensions::Supports("GL_ARB_buffer_storage") ||
 | 
						|
                                          GLExtensions::Supports("GL_EXT_buffer_storage");
 | 
						|
  g_ogl_config.bSupportsMSAA = GLExtensions::Supports("GL_ARB_texture_multisample");
 | 
						|
  g_ogl_config.bSupportViewportFloat = GLExtensions::Supports("GL_ARB_viewport_array");
 | 
						|
  g_ogl_config.bSupportsDebug =
 | 
						|
      GLExtensions::Supports("GL_KHR_debug") || GLExtensions::Supports("GL_ARB_debug_output");
 | 
						|
  g_ogl_config.bSupports3DTextureStorage =
 | 
						|
      GLExtensions::Supports("GL_ARB_texture_storage_multisample") ||
 | 
						|
      GLExtensions::Supports("GL_OES_texture_storage_multisample_2d_array");
 | 
						|
  g_ogl_config.bSupports2DTextureStorage =
 | 
						|
      GLExtensions::Supports("GL_ARB_texture_storage_multisample");
 | 
						|
  g_ogl_config.bSupportsEarlyFragmentTests =
 | 
						|
      GLExtensions::Supports("GL_ARB_shader_image_load_store");
 | 
						|
  g_ogl_config.bSupportsConservativeDepth = GLExtensions::Supports("GL_ARB_conservative_depth");
 | 
						|
  g_ogl_config.bSupportsAniso = GLExtensions::Supports("GL_EXT_texture_filter_anisotropic");
 | 
						|
 | 
						|
  if (GLInterface->GetMode() == GLInterfaceMode::MODE_OPENGLES3)
 | 
						|
  {
 | 
						|
    g_ogl_config.SupportedESPointSize =
 | 
						|
        GLExtensions::Supports("GL_OES_geometry_point_size") ?
 | 
						|
            1 :
 | 
						|
            GLExtensions::Supports("GL_EXT_geometry_point_size") ? 2 : 0;
 | 
						|
    g_ogl_config.SupportedESTextureBuffer = GLExtensions::Supports("VERSION_GLES_3_2") ?
 | 
						|
                                                ES_TEXBUF_TYPE::TEXBUF_CORE :
 | 
						|
                                                GLExtensions::Supports("GL_OES_texture_buffer") ?
 | 
						|
                                                ES_TEXBUF_TYPE::TEXBUF_OES :
 | 
						|
                                                GLExtensions::Supports("GL_EXT_texture_buffer") ?
 | 
						|
                                                ES_TEXBUF_TYPE::TEXBUF_EXT :
 | 
						|
                                                ES_TEXBUF_TYPE::TEXBUF_NONE;
 | 
						|
 | 
						|
    g_ogl_config.bSupportsGLSLCache = true;
 | 
						|
    g_ogl_config.bSupportsGLSync = true;
 | 
						|
 | 
						|
    // TODO: Implement support for GL_EXT_clip_cull_distance when there is an extension for
 | 
						|
    // depth clamping.
 | 
						|
    g_Config.backend_info.bSupportsDepthClamp = false;
 | 
						|
 | 
						|
    if (GLExtensions::Version() == 300)
 | 
						|
    {
 | 
						|
      g_ogl_config.eSupportedGLSLVersion = GLSLES_300;
 | 
						|
      g_ogl_config.bSupportsAEP = false;
 | 
						|
      g_Config.backend_info.bSupportsGeometryShaders = false;
 | 
						|
    }
 | 
						|
    else if (GLExtensions::Version() == 310)
 | 
						|
    {
 | 
						|
      g_ogl_config.eSupportedGLSLVersion = GLSLES_310;
 | 
						|
      g_ogl_config.bSupportsAEP = GLExtensions::Supports("GL_ANDROID_extension_pack_es31a");
 | 
						|
      g_Config.backend_info.bSupportsBindingLayout = true;
 | 
						|
      g_ogl_config.bSupportsEarlyFragmentTests = true;
 | 
						|
      g_Config.backend_info.bSupportsGeometryShaders = g_ogl_config.bSupportsAEP;
 | 
						|
      g_Config.backend_info.bSupportsGSInstancing =
 | 
						|
          g_Config.backend_info.bSupportsGeometryShaders && g_ogl_config.SupportedESPointSize > 0;
 | 
						|
      g_Config.backend_info.bSupportsSSAA = g_ogl_config.bSupportsAEP;
 | 
						|
      g_Config.backend_info.bSupportsBBox = true;
 | 
						|
      g_ogl_config.bSupportsMSAA = true;
 | 
						|
      g_ogl_config.bSupports2DTextureStorage = true;
 | 
						|
      if (g_ActiveConfig.iStereoMode > 0 && g_ActiveConfig.iMultisamples > 1 &&
 | 
						|
          !g_ogl_config.bSupports3DTextureStorage)
 | 
						|
      {
 | 
						|
        // GLES 3.1 can't support stereo rendering and MSAA
 | 
						|
        OSD::AddMessage("MSAA Stereo rendering isn't supported by your GPU.", 10000);
 | 
						|
        g_ActiveConfig.iMultisamples = 1;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
      g_ogl_config.eSupportedGLSLVersion = GLSLES_320;
 | 
						|
      g_ogl_config.bSupportsAEP = GLExtensions::Supports("GL_ANDROID_extension_pack_es31a");
 | 
						|
      g_Config.backend_info.bSupportsBindingLayout = true;
 | 
						|
      g_ogl_config.bSupportsEarlyFragmentTests = true;
 | 
						|
      g_Config.backend_info.bSupportsGeometryShaders = true;
 | 
						|
      g_Config.backend_info.bSupportsGSInstancing = g_ogl_config.SupportedESPointSize > 0;
 | 
						|
      g_Config.backend_info.bSupportsPaletteConversion = true;
 | 
						|
      g_Config.backend_info.bSupportsSSAA = true;
 | 
						|
      g_Config.backend_info.bSupportsBBox = true;
 | 
						|
      g_ogl_config.bSupportsCopySubImage = true;
 | 
						|
      g_ogl_config.bSupportsGLBaseVertex = true;
 | 
						|
      g_ogl_config.bSupportsDebug = true;
 | 
						|
      g_ogl_config.bSupportsMSAA = true;
 | 
						|
      g_ogl_config.bSupports2DTextureStorage = true;
 | 
						|
      g_ogl_config.bSupports3DTextureStorage = true;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    if (GLExtensions::Version() < 300)
 | 
						|
    {
 | 
						|
      PanicAlert("GPU: OGL ERROR: Need at least GLSL 1.30\n"
 | 
						|
                 "GPU: Does your video card support OpenGL 3.0?\n"
 | 
						|
                 "GPU: Your driver supports GLSL %s",
 | 
						|
                 (const char*)glGetString(GL_SHADING_LANGUAGE_VERSION));
 | 
						|
      bSuccess = false;
 | 
						|
    }
 | 
						|
    else if (GLExtensions::Version() == 300)
 | 
						|
    {
 | 
						|
      g_ogl_config.eSupportedGLSLVersion = GLSL_130;
 | 
						|
      g_ogl_config.bSupportsEarlyFragmentTests =
 | 
						|
          false;  // layout keyword is only supported on glsl150+
 | 
						|
      g_ogl_config.bSupportsConservativeDepth =
 | 
						|
          false;  // layout keyword is only supported on glsl150+
 | 
						|
      g_Config.backend_info.bSupportsGeometryShaders =
 | 
						|
          false;  // geometry shaders are only supported on glsl150+
 | 
						|
    }
 | 
						|
    else if (GLExtensions::Version() == 310)
 | 
						|
    {
 | 
						|
      g_ogl_config.eSupportedGLSLVersion = GLSL_140;
 | 
						|
      g_ogl_config.bSupportsEarlyFragmentTests =
 | 
						|
          false;  // layout keyword is only supported on glsl150+
 | 
						|
      g_ogl_config.bSupportsConservativeDepth =
 | 
						|
          false;  // layout keyword is only supported on glsl150+
 | 
						|
      g_Config.backend_info.bSupportsGeometryShaders =
 | 
						|
          false;  // geometry shaders are only supported on glsl150+
 | 
						|
    }
 | 
						|
    else if (GLExtensions::Version() == 320)
 | 
						|
    {
 | 
						|
      g_ogl_config.eSupportedGLSLVersion = GLSL_150;
 | 
						|
    }
 | 
						|
    else if (GLExtensions::Version() == 330)
 | 
						|
    {
 | 
						|
      g_ogl_config.eSupportedGLSLVersion = GLSL_330;
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
      g_ogl_config.eSupportedGLSLVersion = GLSL_400;
 | 
						|
      g_Config.backend_info.bSupportsSSAA = true;
 | 
						|
    }
 | 
						|
 | 
						|
    // Desktop OpenGL can't have the Android Extension Pack
 | 
						|
    g_ogl_config.bSupportsAEP = false;
 | 
						|
  }
 | 
						|
 | 
						|
  // Either method can do early-z tests. See PixelShaderGen for details.
 | 
						|
  g_Config.backend_info.bSupportsEarlyZ =
 | 
						|
      g_ogl_config.bSupportsEarlyFragmentTests || g_ogl_config.bSupportsConservativeDepth;
 | 
						|
 | 
						|
  if (g_ogl_config.bSupportsDebug)
 | 
						|
  {
 | 
						|
    if (GLExtensions::Supports("GL_KHR_debug"))
 | 
						|
    {
 | 
						|
      glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, nullptr, true);
 | 
						|
      glDebugMessageCallback(ErrorCallback, nullptr);
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
      glDebugMessageControlARB(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, nullptr, true);
 | 
						|
      glDebugMessageCallbackARB(ErrorCallback, nullptr);
 | 
						|
    }
 | 
						|
    if (LogManager::GetInstance()->IsEnabled(LogTypes::HOST_GPU, LogTypes::LERROR))
 | 
						|
      glEnable(GL_DEBUG_OUTPUT);
 | 
						|
    else
 | 
						|
      glDisable(GL_DEBUG_OUTPUT);
 | 
						|
  }
 | 
						|
 | 
						|
  int samples;
 | 
						|
  glGetIntegerv(GL_SAMPLES, &samples);
 | 
						|
  if (samples > 1)
 | 
						|
  {
 | 
						|
    // MSAA on default framebuffer isn't working because of glBlitFramebuffer.
 | 
						|
    // It also isn't useful as we don't render anything to the default framebuffer.
 | 
						|
    // We also try to get a non-msaa fb, so this only happens when forced by the driver.
 | 
						|
    PanicAlert("MSAA on default framebuffer isn't supported.\n"
 | 
						|
               "Please avoid forcing Dolphin to use MSAA by the driver.\n"
 | 
						|
               "%d samples on default framebuffer found.",
 | 
						|
               samples);
 | 
						|
    bSuccess = false;
 | 
						|
  }
 | 
						|
 | 
						|
  if (!bSuccess)
 | 
						|
  {
 | 
						|
    // Not all needed extensions are supported, so we have to stop here.
 | 
						|
    // Else some of the next calls might crash.
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  glGetIntegerv(GL_MAX_SAMPLES, &g_ogl_config.max_samples);
 | 
						|
  if (g_ogl_config.max_samples < 1 || !g_ogl_config.bSupportsMSAA)
 | 
						|
    g_ogl_config.max_samples = 1;
 | 
						|
 | 
						|
  g_Config.VerifyValidity();
 | 
						|
  UpdateActiveConfig();
 | 
						|
 | 
						|
  OSD::AddMessage(StringFromFormat("Video Info: %s, %s, %s", g_ogl_config.gl_vendor,
 | 
						|
                                   g_ogl_config.gl_renderer, g_ogl_config.gl_version),
 | 
						|
                  5000);
 | 
						|
 | 
						|
  WARN_LOG(VIDEO, "Missing OGL Extensions: %s%s%s%s%s%s%s%s%s%s%s%s%s%s",
 | 
						|
           g_ActiveConfig.backend_info.bSupportsDualSourceBlend ? "" : "DualSourceBlend ",
 | 
						|
           g_ActiveConfig.backend_info.bSupportsPrimitiveRestart ? "" : "PrimitiveRestart ",
 | 
						|
           g_ActiveConfig.backend_info.bSupportsEarlyZ ? "" : "EarlyZ ",
 | 
						|
           g_ogl_config.bSupportsGLPinnedMemory ? "" : "PinnedMemory ",
 | 
						|
           g_ogl_config.bSupportsGLSLCache ? "" : "ShaderCache ",
 | 
						|
           g_ogl_config.bSupportsGLBaseVertex ? "" : "BaseVertex ",
 | 
						|
           g_ogl_config.bSupportsGLBufferStorage ? "" : "BufferStorage ",
 | 
						|
           g_ogl_config.bSupportsGLSync ? "" : "Sync ", g_ogl_config.bSupportsMSAA ? "" : "MSAA ",
 | 
						|
           g_ActiveConfig.backend_info.bSupportsSSAA ? "" : "SSAA ",
 | 
						|
           g_ActiveConfig.backend_info.bSupportsGSInstancing ? "" : "GSInstancing ",
 | 
						|
           g_ActiveConfig.backend_info.bSupportsClipControl ? "" : "ClipControl ",
 | 
						|
           g_ogl_config.bSupportsCopySubImage ? "" : "CopyImageSubData ",
 | 
						|
           g_ActiveConfig.backend_info.bSupportsDepthClamp ? "" : "DepthClamp ");
 | 
						|
 | 
						|
  s_last_multisamples = g_ActiveConfig.iMultisamples;
 | 
						|
  s_MSAASamples = s_last_multisamples;
 | 
						|
 | 
						|
  s_last_stereo_mode = g_ActiveConfig.iStereoMode > 0;
 | 
						|
  s_last_xfb_mode = g_ActiveConfig.bUseRealXFB;
 | 
						|
 | 
						|
  // Decide framebuffer size
 | 
						|
  s_backbuffer_width = static_cast<int>(std::max(GLInterface->GetBackBufferWidth(), 1u));
 | 
						|
  s_backbuffer_height = static_cast<int>(std::max(GLInterface->GetBackBufferHeight(), 1u));
 | 
						|
 | 
						|
  // Handle VSync on/off
 | 
						|
  s_vsync = g_ActiveConfig.IsVSync();
 | 
						|
  if (!DriverDetails::HasBug(DriverDetails::BUG_BROKEN_VSYNC))
 | 
						|
    GLInterface->SwapInterval(s_vsync);
 | 
						|
 | 
						|
  // TODO: Move these somewhere else?
 | 
						|
  FramebufferManagerBase::SetLastXfbWidth(MAX_XFB_WIDTH);
 | 
						|
  FramebufferManagerBase::SetLastXfbHeight(MAX_XFB_HEIGHT);
 | 
						|
 | 
						|
  UpdateDrawRectangle();
 | 
						|
 | 
						|
  s_last_efb_scale = g_ActiveConfig.iEFBScale;
 | 
						|
  CalculateTargetSize();
 | 
						|
 | 
						|
  PixelShaderManager::SetEfbScaleChanged();
 | 
						|
 | 
						|
  // Because of the fixed framebuffer size we need to disable the resolution
 | 
						|
  // options while running
 | 
						|
  g_Config.bRunning = true;
 | 
						|
 | 
						|
  glStencilFunc(GL_ALWAYS, 0, 0);
 | 
						|
  glBlendFunc(GL_ONE, GL_ONE);
 | 
						|
 | 
						|
  glViewport(0, 0, GetTargetWidth(), GetTargetHeight());  // Reset The Current Viewport
 | 
						|
  if (g_ActiveConfig.backend_info.bSupportsClipControl)
 | 
						|
    glClipControl(GL_LOWER_LEFT, GL_ZERO_TO_ONE);
 | 
						|
 | 
						|
  glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
 | 
						|
  glClearDepthf(1.0f);
 | 
						|
  glEnable(GL_DEPTH_TEST);
 | 
						|
  glDepthFunc(GL_LEQUAL);
 | 
						|
  if (g_ActiveConfig.backend_info.bSupportsDepthClamp)
 | 
						|
  {
 | 
						|
    glEnable(GL_CLIP_DISTANCE0);
 | 
						|
    glEnable(GL_CLIP_DISTANCE1);
 | 
						|
    glEnable(GL_DEPTH_CLAMP);
 | 
						|
  }
 | 
						|
 | 
						|
  glPixelStorei(GL_UNPACK_ALIGNMENT, 4);  // 4-byte pixel alignment
 | 
						|
 | 
						|
  glDisable(GL_STENCIL_TEST);
 | 
						|
  glEnable(GL_SCISSOR_TEST);
 | 
						|
 | 
						|
  glScissor(0, 0, GetTargetWidth(), GetTargetHeight());
 | 
						|
  glBlendColor(0, 0, 0, 0.5f);
 | 
						|
  glClearDepthf(1.0f);
 | 
						|
 | 
						|
  if (g_ActiveConfig.backend_info.bSupportsPrimitiveRestart)
 | 
						|
  {
 | 
						|
    if (GLInterface->GetMode() == GLInterfaceMode::MODE_OPENGLES3)
 | 
						|
    {
 | 
						|
      glEnable(GL_PRIMITIVE_RESTART_FIXED_INDEX);
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
      if (GLExtensions::Version() >= 310)
 | 
						|
      {
 | 
						|
        glEnable(GL_PRIMITIVE_RESTART);
 | 
						|
        glPrimitiveRestartIndex(65535);
 | 
						|
      }
 | 
						|
      else
 | 
						|
      {
 | 
						|
        glEnableClientState(GL_PRIMITIVE_RESTART_NV);
 | 
						|
        glPrimitiveRestartIndexNV(65535);
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
  IndexGenerator::Init();
 | 
						|
 | 
						|
  UpdateActiveConfig();
 | 
						|
  ClearEFBCache();
 | 
						|
}
 | 
						|
 | 
						|
Renderer::~Renderer()
 | 
						|
{
 | 
						|
  FlushFrameDump();
 | 
						|
  FinishFrameData();
 | 
						|
  DestroyFrameDumpResources();
 | 
						|
}
 | 
						|
 | 
						|
void Renderer::Shutdown()
 | 
						|
{
 | 
						|
  g_framebuffer_manager.reset();
 | 
						|
 | 
						|
  g_Config.bRunning = false;
 | 
						|
  UpdateActiveConfig();
 | 
						|
 | 
						|
  s_raster_font.reset();
 | 
						|
  m_post_processor.reset();
 | 
						|
 | 
						|
  OpenGL_DeleteAttributelessVAO();
 | 
						|
}
 | 
						|
 | 
						|
void Renderer::Init()
 | 
						|
{
 | 
						|
  // Initialize the FramebufferManager
 | 
						|
  g_framebuffer_manager =
 | 
						|
      std::make_unique<FramebufferManager>(s_target_width, s_target_height, s_MSAASamples);
 | 
						|
 | 
						|
  m_post_processor = std::make_unique<OpenGLPostProcessing>();
 | 
						|
  s_raster_font = std::make_unique<RasterFont>();
 | 
						|
 | 
						|
  OpenGL_CreateAttributelessVAO();
 | 
						|
}
 | 
						|
 | 
						|
void Renderer::RenderText(const std::string& text, int left, int top, u32 color)
 | 
						|
{
 | 
						|
  u32 backbuffer_width = std::max(GLInterface->GetBackBufferWidth(), 1u);
 | 
						|
  u32 backbuffer_height = std::max(GLInterface->GetBackBufferHeight(), 1u);
 | 
						|
 | 
						|
  s_raster_font->printMultilineText(text, left * 2.0f / static_cast<float>(backbuffer_width) - 1.0f,
 | 
						|
                                    1.0f - top * 2.0f / static_cast<float>(backbuffer_height), 0,
 | 
						|
                                    backbuffer_width, backbuffer_height, color);
 | 
						|
}
 | 
						|
 | 
						|
TargetRectangle Renderer::ConvertEFBRectangle(const EFBRectangle& rc)
 | 
						|
{
 | 
						|
  TargetRectangle result;
 | 
						|
  result.left = EFBToScaledX(rc.left);
 | 
						|
  result.top = EFBToScaledY(EFB_HEIGHT - rc.top);
 | 
						|
  result.right = EFBToScaledX(rc.right);
 | 
						|
  result.bottom = EFBToScaledY(EFB_HEIGHT - rc.bottom);
 | 
						|
  return result;
 | 
						|
}
 | 
						|
 | 
						|
// Function: This function handles the OpenGL glScissor() function
 | 
						|
// ----------------------------
 | 
						|
// Call browser: OpcodeDecoding.cpp ExecuteDisplayList > Decode() > LoadBPReg()
 | 
						|
//		case 0x52 > SetScissorRect()
 | 
						|
// ----------------------------
 | 
						|
// bpmem.scissorTL.x, y = 342x342
 | 
						|
// bpmem.scissorBR.x, y = 981x821
 | 
						|
// Renderer::GetTargetHeight() = the fixed ini file setting
 | 
						|
// donkopunchstania - it appears scissorBR is the bottom right pixel inside the scissor box
 | 
						|
// therefore the width and height are (scissorBR + 1) - scissorTL
 | 
						|
void Renderer::SetScissorRect(const EFBRectangle& rc)
 | 
						|
{
 | 
						|
  TargetRectangle trc = ConvertEFBRectangle(rc);
 | 
						|
  glScissor(trc.left, trc.bottom, trc.GetWidth(), trc.GetHeight());
 | 
						|
}
 | 
						|
 | 
						|
void ClearEFBCache()
 | 
						|
{
 | 
						|
  if (!s_efbCacheIsCleared)
 | 
						|
  {
 | 
						|
    s_efbCacheIsCleared = true;
 | 
						|
    memset(s_efbCacheValid, 0, sizeof(s_efbCacheValid));
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void Renderer::UpdateEFBCache(EFBAccessType type, u32 cacheRectIdx, const EFBRectangle& efbPixelRc,
 | 
						|
                              const TargetRectangle& targetPixelRc, const void* data)
 | 
						|
{
 | 
						|
  u32 cacheType = (type == PEEK_Z ? 0 : 1);
 | 
						|
 | 
						|
  if (!s_efbCache[cacheType][cacheRectIdx].size())
 | 
						|
    s_efbCache[cacheType][cacheRectIdx].resize(EFB_CACHE_RECT_SIZE * EFB_CACHE_RECT_SIZE);
 | 
						|
 | 
						|
  u32 targetPixelRcWidth = targetPixelRc.right - targetPixelRc.left;
 | 
						|
  u32 efbPixelRcHeight = efbPixelRc.bottom - efbPixelRc.top;
 | 
						|
  u32 efbPixelRcWidth = efbPixelRc.right - efbPixelRc.left;
 | 
						|
 | 
						|
  for (u32 yCache = 0; yCache < efbPixelRcHeight; ++yCache)
 | 
						|
  {
 | 
						|
    u32 yEFB = efbPixelRc.top + yCache;
 | 
						|
    u32 yPixel = (EFBToScaledY(EFB_HEIGHT - yEFB) + EFBToScaledY(EFB_HEIGHT - yEFB - 1)) / 2;
 | 
						|
    u32 yData = yPixel - targetPixelRc.bottom;
 | 
						|
 | 
						|
    for (u32 xCache = 0; xCache < efbPixelRcWidth; ++xCache)
 | 
						|
    {
 | 
						|
      u32 xEFB = efbPixelRc.left + xCache;
 | 
						|
      u32 xPixel = (EFBToScaledX(xEFB) + EFBToScaledX(xEFB + 1)) / 2;
 | 
						|
      u32 xData = xPixel - targetPixelRc.left;
 | 
						|
      u32 value;
 | 
						|
      if (type == PEEK_Z)
 | 
						|
      {
 | 
						|
        float* ptr = (float*)data;
 | 
						|
        value = MathUtil::Clamp<u32>((u32)(ptr[yData * targetPixelRcWidth + xData] * 16777216.0f),
 | 
						|
                                     0, 0xFFFFFF);
 | 
						|
      }
 | 
						|
      else
 | 
						|
      {
 | 
						|
        u32* ptr = (u32*)data;
 | 
						|
        value = ptr[yData * targetPixelRcWidth + xData];
 | 
						|
      }
 | 
						|
      s_efbCache[cacheType][cacheRectIdx][yCache * EFB_CACHE_RECT_SIZE + xCache] = value;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  s_efbCacheValid[cacheType][cacheRectIdx] = true;
 | 
						|
  s_efbCacheIsCleared = false;
 | 
						|
}
 | 
						|
 | 
						|
// This function allows the CPU to directly access the EFB.
 | 
						|
// There are EFB peeks (which will read the color or depth of a pixel)
 | 
						|
// and EFB pokes (which will change the color or depth of a pixel).
 | 
						|
//
 | 
						|
// The behavior of EFB peeks can only be modified by:
 | 
						|
// - GX_PokeAlphaRead
 | 
						|
// The behavior of EFB pokes can be modified by:
 | 
						|
// - GX_PokeAlphaMode (TODO)
 | 
						|
// - GX_PokeAlphaUpdate (TODO)
 | 
						|
// - GX_PokeBlendMode (TODO)
 | 
						|
// - GX_PokeColorUpdate (TODO)
 | 
						|
// - GX_PokeDither (TODO)
 | 
						|
// - GX_PokeDstAlpha (TODO)
 | 
						|
// - GX_PokeZMode (TODO)
 | 
						|
u32 Renderer::AccessEFB(EFBAccessType type, u32 x, u32 y, u32 poke_data)
 | 
						|
{
 | 
						|
  u32 cacheRectIdx = (y / EFB_CACHE_RECT_SIZE) * EFB_CACHE_WIDTH + (x / EFB_CACHE_RECT_SIZE);
 | 
						|
 | 
						|
  EFBRectangle efbPixelRc;
 | 
						|
 | 
						|
  if (type == PEEK_COLOR || type == PEEK_Z)
 | 
						|
  {
 | 
						|
    // Get the rectangular target region containing the EFB pixel
 | 
						|
    efbPixelRc.left = (x / EFB_CACHE_RECT_SIZE) * EFB_CACHE_RECT_SIZE;
 | 
						|
    efbPixelRc.top = (y / EFB_CACHE_RECT_SIZE) * EFB_CACHE_RECT_SIZE;
 | 
						|
    efbPixelRc.right = std::min(efbPixelRc.left + EFB_CACHE_RECT_SIZE, (u32)EFB_WIDTH);
 | 
						|
    efbPixelRc.bottom = std::min(efbPixelRc.top + EFB_CACHE_RECT_SIZE, (u32)EFB_HEIGHT);
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    efbPixelRc.left = x;
 | 
						|
    efbPixelRc.top = y;
 | 
						|
    efbPixelRc.right = x + 1;
 | 
						|
    efbPixelRc.bottom = y + 1;
 | 
						|
  }
 | 
						|
 | 
						|
  TargetRectangle targetPixelRc = ConvertEFBRectangle(efbPixelRc);
 | 
						|
  u32 targetPixelRcWidth = targetPixelRc.right - targetPixelRc.left;
 | 
						|
  u32 targetPixelRcHeight = targetPixelRc.top - targetPixelRc.bottom;
 | 
						|
 | 
						|
  // TODO (FIX) : currently, AA path is broken/offset and doesn't return the correct pixel
 | 
						|
  switch (type)
 | 
						|
  {
 | 
						|
  case PEEK_Z:
 | 
						|
  {
 | 
						|
    if (!s_efbCacheValid[0][cacheRectIdx])
 | 
						|
    {
 | 
						|
      if (s_MSAASamples > 1)
 | 
						|
      {
 | 
						|
        ResetAPIState();
 | 
						|
 | 
						|
        // Resolve our rectangle.
 | 
						|
        FramebufferManager::GetEFBDepthTexture(efbPixelRc);
 | 
						|
        glBindFramebuffer(GL_READ_FRAMEBUFFER, FramebufferManager::GetResolvedFramebuffer());
 | 
						|
 | 
						|
        RestoreAPIState();
 | 
						|
      }
 | 
						|
 | 
						|
      std::unique_ptr<float[]> depthMap(new float[targetPixelRcWidth * targetPixelRcHeight]);
 | 
						|
 | 
						|
      glReadPixels(targetPixelRc.left, targetPixelRc.bottom, targetPixelRcWidth,
 | 
						|
                   targetPixelRcHeight, GL_DEPTH_COMPONENT, GL_FLOAT, depthMap.get());
 | 
						|
 | 
						|
      UpdateEFBCache(type, cacheRectIdx, efbPixelRc, targetPixelRc, depthMap.get());
 | 
						|
    }
 | 
						|
 | 
						|
    u32 xRect = x % EFB_CACHE_RECT_SIZE;
 | 
						|
    u32 yRect = y % EFB_CACHE_RECT_SIZE;
 | 
						|
    u32 z = s_efbCache[0][cacheRectIdx][yRect * EFB_CACHE_RECT_SIZE + xRect];
 | 
						|
 | 
						|
    // if Z is in 16 bit format you must return a 16 bit integer
 | 
						|
    if (bpmem.zcontrol.pixel_format == PEControl::RGB565_Z16)
 | 
						|
      z = z >> 8;
 | 
						|
 | 
						|
    return z;
 | 
						|
  }
 | 
						|
 | 
						|
  case PEEK_COLOR:  // GXPeekARGB
 | 
						|
  {
 | 
						|
    // Although it may sound strange, this really is A8R8G8B8 and not RGBA or 24-bit...
 | 
						|
 | 
						|
    // Tested in Killer 7, the first 8bits represent the alpha value which is used to
 | 
						|
    // determine if we're aiming at an enemy (0x80 / 0x88) or not (0x70)
 | 
						|
    // Wind Waker is also using it for the pictograph to determine the color of each pixel
 | 
						|
    if (!s_efbCacheValid[1][cacheRectIdx])
 | 
						|
    {
 | 
						|
      if (s_MSAASamples > 1)
 | 
						|
      {
 | 
						|
        ResetAPIState();
 | 
						|
 | 
						|
        // Resolve our rectangle.
 | 
						|
        FramebufferManager::GetEFBColorTexture(efbPixelRc);
 | 
						|
        glBindFramebuffer(GL_READ_FRAMEBUFFER, FramebufferManager::GetResolvedFramebuffer());
 | 
						|
 | 
						|
        RestoreAPIState();
 | 
						|
      }
 | 
						|
 | 
						|
      std::unique_ptr<u32[]> colorMap(new u32[targetPixelRcWidth * targetPixelRcHeight]);
 | 
						|
 | 
						|
      if (GLInterface->GetMode() == GLInterfaceMode::MODE_OPENGLES3)
 | 
						|
        // XXX: Swap colours
 | 
						|
        glReadPixels(targetPixelRc.left, targetPixelRc.bottom, targetPixelRcWidth,
 | 
						|
                     targetPixelRcHeight, GL_RGBA, GL_UNSIGNED_BYTE, colorMap.get());
 | 
						|
      else
 | 
						|
        glReadPixels(targetPixelRc.left, targetPixelRc.bottom, targetPixelRcWidth,
 | 
						|
                     targetPixelRcHeight, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, colorMap.get());
 | 
						|
 | 
						|
      UpdateEFBCache(type, cacheRectIdx, efbPixelRc, targetPixelRc, colorMap.get());
 | 
						|
    }
 | 
						|
 | 
						|
    u32 xRect = x % EFB_CACHE_RECT_SIZE;
 | 
						|
    u32 yRect = y % EFB_CACHE_RECT_SIZE;
 | 
						|
    u32 color = s_efbCache[1][cacheRectIdx][yRect * EFB_CACHE_RECT_SIZE + xRect];
 | 
						|
 | 
						|
    // check what to do with the alpha channel (GX_PokeAlphaRead)
 | 
						|
    PixelEngine::UPEAlphaReadReg alpha_read_mode = PixelEngine::GetAlphaReadMode();
 | 
						|
 | 
						|
    if (bpmem.zcontrol.pixel_format == PEControl::RGBA6_Z24)
 | 
						|
    {
 | 
						|
      color = RGBA8ToRGBA6ToRGBA8(color);
 | 
						|
    }
 | 
						|
    else if (bpmem.zcontrol.pixel_format == PEControl::RGB565_Z16)
 | 
						|
    {
 | 
						|
      color = RGBA8ToRGB565ToRGBA8(color);
 | 
						|
    }
 | 
						|
    if (bpmem.zcontrol.pixel_format != PEControl::RGBA6_Z24)
 | 
						|
    {
 | 
						|
      color |= 0xFF000000;
 | 
						|
    }
 | 
						|
    if (alpha_read_mode.ReadMode == 2)
 | 
						|
    {
 | 
						|
      // GX_READ_NONE
 | 
						|
      return color;
 | 
						|
    }
 | 
						|
    else if (alpha_read_mode.ReadMode == 1)
 | 
						|
    {
 | 
						|
      // GX_READ_FF
 | 
						|
      return (color | 0xFF000000);
 | 
						|
    }
 | 
						|
    else /*if(alpha_read_mode.ReadMode == 0)*/
 | 
						|
    {
 | 
						|
      // GX_READ_00
 | 
						|
      return (color & 0x00FFFFFF);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  default:
 | 
						|
    break;
 | 
						|
  }
 | 
						|
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
void Renderer::PokeEFB(EFBAccessType type, const EfbPokeData* points, size_t num_points)
 | 
						|
{
 | 
						|
  FramebufferManager::PokeEFB(type, points, num_points);
 | 
						|
}
 | 
						|
 | 
						|
u16 Renderer::BBoxRead(int index)
 | 
						|
{
 | 
						|
  int swapped_index = index;
 | 
						|
  if (index >= 2)
 | 
						|
    swapped_index ^= 1;  // swap 2 and 3 for top/bottom
 | 
						|
 | 
						|
  // Here we get the min/max value of the truncated position of the upscaled and swapped
 | 
						|
  // framebuffer.
 | 
						|
  // So we have to correct them to the unscaled EFB sizes.
 | 
						|
  int value = BoundingBox::Get(swapped_index);
 | 
						|
 | 
						|
  if (index < 2)
 | 
						|
  {
 | 
						|
    // left/right
 | 
						|
    value = value * EFB_WIDTH / s_target_width;
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    // up/down -- we have to swap up and down
 | 
						|
    value = value * EFB_HEIGHT / s_target_height;
 | 
						|
    value = EFB_HEIGHT - value - 1;
 | 
						|
  }
 | 
						|
  if (index & 1)
 | 
						|
    value++;  // fix max values to describe the outer border
 | 
						|
 | 
						|
  return value;
 | 
						|
}
 | 
						|
 | 
						|
void Renderer::BBoxWrite(int index, u16 _value)
 | 
						|
{
 | 
						|
  int value = _value;  // u16 isn't enough to multiply by the efb width
 | 
						|
  if (index & 1)
 | 
						|
    value--;
 | 
						|
  if (index < 2)
 | 
						|
  {
 | 
						|
    value = value * s_target_width / EFB_WIDTH;
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    index ^= 1;  // swap 2 and 3 for top/bottom
 | 
						|
    value = EFB_HEIGHT - value - 1;
 | 
						|
    value = value * s_target_height / EFB_HEIGHT;
 | 
						|
  }
 | 
						|
 | 
						|
  BoundingBox::Set(index, value);
 | 
						|
}
 | 
						|
 | 
						|
void Renderer::SetViewport()
 | 
						|
{
 | 
						|
  // reversed gxsetviewport(xorig, yorig, width, height, nearz, farz)
 | 
						|
  // [0] = width/2
 | 
						|
  // [1] = height/2
 | 
						|
  // [2] = 16777215 * (farz - nearz)
 | 
						|
  // [3] = xorig + width/2 + 342
 | 
						|
  // [4] = yorig + height/2 + 342
 | 
						|
  // [5] = 16777215 * farz
 | 
						|
 | 
						|
  int scissorXOff = bpmem.scissorOffset.x * 2;
 | 
						|
  int scissorYOff = bpmem.scissorOffset.y * 2;
 | 
						|
 | 
						|
  // TODO: ceil, floor or just cast to int?
 | 
						|
  float X = EFBToScaledXf(xfmem.viewport.xOrig - xfmem.viewport.wd - (float)scissorXOff);
 | 
						|
  float Y = EFBToScaledYf((float)EFB_HEIGHT - xfmem.viewport.yOrig + xfmem.viewport.ht +
 | 
						|
                          (float)scissorYOff);
 | 
						|
  float Width = EFBToScaledXf(2.0f * xfmem.viewport.wd);
 | 
						|
  float Height = EFBToScaledYf(-2.0f * xfmem.viewport.ht);
 | 
						|
  float range = MathUtil::Clamp<float>(xfmem.viewport.zRange, -16777215.0f, 16777215.0f);
 | 
						|
  float min_depth =
 | 
						|
      MathUtil::Clamp<float>(xfmem.viewport.farZ - range, 0.0f, 16777215.0f) / 16777216.0f;
 | 
						|
  float max_depth = MathUtil::Clamp<float>(xfmem.viewport.farZ, 0.0f, 16777215.0f) / 16777216.0f;
 | 
						|
  if (Width < 0)
 | 
						|
  {
 | 
						|
    X += Width;
 | 
						|
    Width *= -1;
 | 
						|
  }
 | 
						|
  if (Height < 0)
 | 
						|
  {
 | 
						|
    Y += Height;
 | 
						|
    Height *= -1;
 | 
						|
  }
 | 
						|
 | 
						|
  // Update the view port
 | 
						|
  if (g_ogl_config.bSupportViewportFloat)
 | 
						|
  {
 | 
						|
    glViewportIndexedf(0, X, Y, Width, Height);
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    auto iceilf = [](float f) { return static_cast<GLint>(ceilf(f)); };
 | 
						|
    glViewport(iceilf(X), iceilf(Y), iceilf(Width), iceilf(Height));
 | 
						|
  }
 | 
						|
 | 
						|
  // Set the reversed depth range. If we do depth clipping and depth range in the
 | 
						|
  // vertex shader we only need to ensure depth values don't exceed the maximum
 | 
						|
  // value supported by the console GPU. If not, we simply clamp the near/far values
 | 
						|
  // themselves to the maximum value as done above.
 | 
						|
  glDepthRangef(max_depth, min_depth);
 | 
						|
}
 | 
						|
 | 
						|
void Renderer::ClearScreen(const EFBRectangle& rc, bool colorEnable, bool alphaEnable, bool zEnable,
 | 
						|
                           u32 color, u32 z)
 | 
						|
{
 | 
						|
  ResetAPIState();
 | 
						|
 | 
						|
  // color
 | 
						|
  GLboolean const color_mask = colorEnable ? GL_TRUE : GL_FALSE,
 | 
						|
                  alpha_mask = alphaEnable ? GL_TRUE : GL_FALSE;
 | 
						|
  glColorMask(color_mask, color_mask, color_mask, alpha_mask);
 | 
						|
 | 
						|
  glClearColor(float((color >> 16) & 0xFF) / 255.0f, float((color >> 8) & 0xFF) / 255.0f,
 | 
						|
               float((color >> 0) & 0xFF) / 255.0f, float((color >> 24) & 0xFF) / 255.0f);
 | 
						|
 | 
						|
  // depth
 | 
						|
  glDepthMask(zEnable ? GL_TRUE : GL_FALSE);
 | 
						|
 | 
						|
  glClearDepthf(float(z & 0xFFFFFF) / 16777216.0f);
 | 
						|
 | 
						|
  // Update rect for clearing the picture
 | 
						|
  glEnable(GL_SCISSOR_TEST);
 | 
						|
 | 
						|
  TargetRectangle const targetRc = ConvertEFBRectangle(rc);
 | 
						|
  glScissor(targetRc.left, targetRc.bottom, targetRc.GetWidth(), targetRc.GetHeight());
 | 
						|
 | 
						|
  // glColorMask/glDepthMask/glScissor affect glClear (glViewport does not)
 | 
						|
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
 | 
						|
 | 
						|
  RestoreAPIState();
 | 
						|
 | 
						|
  ClearEFBCache();
 | 
						|
}
 | 
						|
 | 
						|
void Renderer::BlitScreen(TargetRectangle src, TargetRectangle dst, GLuint src_texture,
 | 
						|
                          int src_width, int src_height)
 | 
						|
{
 | 
						|
  if (g_ActiveConfig.iStereoMode == STEREO_SBS || g_ActiveConfig.iStereoMode == STEREO_TAB)
 | 
						|
  {
 | 
						|
    TargetRectangle leftRc, rightRc;
 | 
						|
 | 
						|
    // Top-and-Bottom mode needs to compensate for inverted vertical screen coordinates.
 | 
						|
    if (g_ActiveConfig.iStereoMode == STEREO_TAB)
 | 
						|
      ConvertStereoRectangle(dst, rightRc, leftRc);
 | 
						|
    else
 | 
						|
      ConvertStereoRectangle(dst, leftRc, rightRc);
 | 
						|
 | 
						|
    m_post_processor->BlitFromTexture(src, leftRc, src_texture, src_width, src_height, 0);
 | 
						|
    m_post_processor->BlitFromTexture(src, rightRc, src_texture, src_width, src_height, 1);
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    m_post_processor->BlitFromTexture(src, dst, src_texture, src_width, src_height);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void Renderer::ReinterpretPixelData(unsigned int convtype)
 | 
						|
{
 | 
						|
  if (convtype == 0 || convtype == 2)
 | 
						|
  {
 | 
						|
    FramebufferManager::ReinterpretPixelData(convtype);
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    ERROR_LOG(VIDEO, "Trying to reinterpret pixel data with unsupported conversion type %d",
 | 
						|
              convtype);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void Renderer::SetBlendMode(bool forceUpdate)
 | 
						|
{
 | 
						|
  BlendingState state;
 | 
						|
  state.Generate(bpmem);
 | 
						|
 | 
						|
  bool useDualSource =
 | 
						|
      g_ActiveConfig.backend_info.bSupportsDualSourceBlend &&
 | 
						|
      (!DriverDetails::HasBug(DriverDetails::BUG_BROKEN_DUAL_SOURCE_BLENDING) || state.dstalpha);
 | 
						|
 | 
						|
  const GLenum src_factors[8] = {
 | 
						|
      GL_ZERO,
 | 
						|
      GL_ONE,
 | 
						|
      GL_DST_COLOR,
 | 
						|
      GL_ONE_MINUS_DST_COLOR,
 | 
						|
      useDualSource ? GL_SRC1_ALPHA : (GLenum)GL_SRC_ALPHA,
 | 
						|
      useDualSource ? GL_ONE_MINUS_SRC1_ALPHA : (GLenum)GL_ONE_MINUS_SRC_ALPHA,
 | 
						|
      GL_DST_ALPHA,
 | 
						|
      GL_ONE_MINUS_DST_ALPHA};
 | 
						|
  const GLenum dst_factors[8] = {
 | 
						|
      GL_ZERO,
 | 
						|
      GL_ONE,
 | 
						|
      GL_SRC_COLOR,
 | 
						|
      GL_ONE_MINUS_SRC_COLOR,
 | 
						|
      useDualSource ? GL_SRC1_ALPHA : (GLenum)GL_SRC_ALPHA,
 | 
						|
      useDualSource ? GL_ONE_MINUS_SRC1_ALPHA : (GLenum)GL_ONE_MINUS_SRC_ALPHA,
 | 
						|
      GL_DST_ALPHA,
 | 
						|
      GL_ONE_MINUS_DST_ALPHA};
 | 
						|
 | 
						|
  if (state.blendenable)
 | 
						|
  {
 | 
						|
    GLenum equation = state.subtract ? GL_FUNC_REVERSE_SUBTRACT : GL_FUNC_ADD;
 | 
						|
    GLenum equationAlpha = state.subtractAlpha ? GL_FUNC_REVERSE_SUBTRACT : GL_FUNC_ADD;
 | 
						|
 | 
						|
    glEnable(GL_BLEND);
 | 
						|
    glBlendEquationSeparate(equation, equationAlpha);
 | 
						|
    glBlendFuncSeparate(src_factors[state.srcfactor], dst_factors[state.dstfactor],
 | 
						|
                        src_factors[state.srcfactoralpha], dst_factors[state.dstfactoralpha]);
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    glDisable(GL_BLEND);
 | 
						|
  }
 | 
						|
 | 
						|
  const GLenum logic_op_codes[16] = {
 | 
						|
      GL_CLEAR,         GL_AND,         GL_AND_REVERSE, GL_COPY,  GL_AND_INVERTED, GL_NOOP,
 | 
						|
      GL_XOR,           GL_OR,          GL_NOR,         GL_EQUIV, GL_INVERT,       GL_OR_REVERSE,
 | 
						|
      GL_COPY_INVERTED, GL_OR_INVERTED, GL_NAND,        GL_SET};
 | 
						|
 | 
						|
  if (GLInterface->GetMode() != GLInterfaceMode::MODE_OPENGL)
 | 
						|
  {
 | 
						|
    // Logic ops aren't available in GLES3
 | 
						|
  }
 | 
						|
  else if (state.logicopenable)
 | 
						|
  {
 | 
						|
    glEnable(GL_COLOR_LOGIC_OP);
 | 
						|
    glLogicOp(logic_op_codes[state.logicmode]);
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    glDisable(GL_COLOR_LOGIC_OP);
 | 
						|
  }
 | 
						|
 | 
						|
  if (state.dither)
 | 
						|
    glEnable(GL_DITHER);
 | 
						|
  else
 | 
						|
    glDisable(GL_DITHER);
 | 
						|
 | 
						|
  glColorMask(state.colorupdate, state.colorupdate, state.colorupdate, state.alphaupdate);
 | 
						|
}
 | 
						|
 | 
						|
// This function has the final picture. We adjust the aspect ratio here.
 | 
						|
void Renderer::SwapImpl(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight,
 | 
						|
                        const EFBRectangle& rc, u64 ticks, float Gamma)
 | 
						|
{
 | 
						|
  if (g_ogl_config.bSupportsDebug)
 | 
						|
  {
 | 
						|
    if (LogManager::GetInstance()->IsEnabled(LogTypes::HOST_GPU, LogTypes::LERROR))
 | 
						|
      glEnable(GL_DEBUG_OUTPUT);
 | 
						|
    else
 | 
						|
      glDisable(GL_DEBUG_OUTPUT);
 | 
						|
  }
 | 
						|
 | 
						|
  if ((!XFBWrited && !g_ActiveConfig.RealXFBEnabled()) || !fbWidth || !fbHeight)
 | 
						|
  {
 | 
						|
    Core::Callback_VideoCopiedToXFB(false);
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  u32 xfbCount = 0;
 | 
						|
  const XFBSourceBase* const* xfbSourceList =
 | 
						|
      FramebufferManager::GetXFBSource(xfbAddr, fbStride, fbHeight, &xfbCount);
 | 
						|
  if (g_ActiveConfig.VirtualXFBEnabled() && (!xfbSourceList || xfbCount == 0))
 | 
						|
  {
 | 
						|
    Core::Callback_VideoCopiedToXFB(false);
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  ResetAPIState();
 | 
						|
 | 
						|
  UpdateDrawRectangle();
 | 
						|
  TargetRectangle flipped_trc = GetTargetRectangle();
 | 
						|
 | 
						|
  // Flip top and bottom for some reason; TODO: Fix the code to suck less?
 | 
						|
  std::swap(flipped_trc.top, flipped_trc.bottom);
 | 
						|
 | 
						|
  // Copy the framebuffer to screen.
 | 
						|
  DrawFrame(0, flipped_trc, rc, xfbAddr, xfbSourceList, xfbCount, fbWidth, fbStride, fbHeight);
 | 
						|
 | 
						|
  // The FlushFrameDump call here is necessary even after frame dumping is stopped.
 | 
						|
  // If left out, screenshots are "one frame" behind, as an extra frame is dumped and buffered.
 | 
						|
  FlushFrameDump();
 | 
						|
  if (IsFrameDumping())
 | 
						|
  {
 | 
						|
    // Currently, we only use the off-screen buffer as a frame dump source if full-resolution
 | 
						|
    // frame dumping is enabled, saving the need for an extra copy. In the future, this could
 | 
						|
    // be extended to be used for surfaceless contexts as well.
 | 
						|
    bool use_offscreen_buffer = g_ActiveConfig.bInternalResolutionFrameDumps;
 | 
						|
    if (use_offscreen_buffer)
 | 
						|
    {
 | 
						|
      // DumpFrameUsingFBO resets GL_FRAMEBUFFER, so change back to the window for drawing OSD.
 | 
						|
      DumpFrameUsingFBO(rc, xfbAddr, xfbSourceList, xfbCount, fbWidth, fbStride, fbHeight, ticks);
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
      // GL_READ_FRAMEBUFFER is set by GL_FRAMEBUFFER in DrawFrame -> Draw{EFB,VirtualXFB,RealXFB}.
 | 
						|
      DumpFrame(flipped_trc, ticks);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  // Finish up the current frame, print some stats
 | 
						|
 | 
						|
  SetWindowSize(fbStride, fbHeight);
 | 
						|
 | 
						|
  GLInterface->Update();  // just updates the render window position and the backbuffer size
 | 
						|
 | 
						|
  bool xfbchanged = s_last_xfb_mode != g_ActiveConfig.bUseRealXFB;
 | 
						|
 | 
						|
  if (FramebufferManagerBase::LastXfbWidth() != fbStride ||
 | 
						|
      FramebufferManagerBase::LastXfbHeight() != fbHeight)
 | 
						|
  {
 | 
						|
    xfbchanged = true;
 | 
						|
    unsigned int const last_w =
 | 
						|
        (fbStride < 1 || fbStride > MAX_XFB_WIDTH) ? MAX_XFB_WIDTH : fbStride;
 | 
						|
    unsigned int const last_h =
 | 
						|
        (fbHeight < 1 || fbHeight > MAX_XFB_HEIGHT) ? MAX_XFB_HEIGHT : fbHeight;
 | 
						|
    FramebufferManagerBase::SetLastXfbWidth(last_w);
 | 
						|
    FramebufferManagerBase::SetLastXfbHeight(last_h);
 | 
						|
  }
 | 
						|
 | 
						|
  bool window_resized = false;
 | 
						|
  int window_width = static_cast<int>(std::max(GLInterface->GetBackBufferWidth(), 1u));
 | 
						|
  int window_height = static_cast<int>(std::max(GLInterface->GetBackBufferHeight(), 1u));
 | 
						|
  if (window_width != s_backbuffer_width || window_height != s_backbuffer_height ||
 | 
						|
      s_last_efb_scale != g_ActiveConfig.iEFBScale)
 | 
						|
  {
 | 
						|
    window_resized = true;
 | 
						|
    s_backbuffer_width = window_width;
 | 
						|
    s_backbuffer_height = window_height;
 | 
						|
    s_last_efb_scale = g_ActiveConfig.iEFBScale;
 | 
						|
  }
 | 
						|
 | 
						|
  bool target_size_changed = CalculateTargetSize();
 | 
						|
  if (target_size_changed || xfbchanged || window_resized ||
 | 
						|
      (s_last_multisamples != g_ActiveConfig.iMultisamples) ||
 | 
						|
      (s_last_stereo_mode != (g_ActiveConfig.iStereoMode > 0)))
 | 
						|
  {
 | 
						|
    s_last_xfb_mode = g_ActiveConfig.bUseRealXFB;
 | 
						|
 | 
						|
    UpdateDrawRectangle();
 | 
						|
 | 
						|
    if (target_size_changed || s_last_multisamples != g_ActiveConfig.iMultisamples ||
 | 
						|
        s_last_stereo_mode != (g_ActiveConfig.iStereoMode > 0))
 | 
						|
    {
 | 
						|
      s_last_stereo_mode = g_ActiveConfig.iStereoMode > 0;
 | 
						|
      s_last_multisamples = g_ActiveConfig.iMultisamples;
 | 
						|
      s_MSAASamples = s_last_multisamples;
 | 
						|
 | 
						|
      if (s_MSAASamples > 1 && s_MSAASamples > g_ogl_config.max_samples)
 | 
						|
      {
 | 
						|
        s_MSAASamples = g_ogl_config.max_samples;
 | 
						|
        OSD::AddMessage(StringFromFormat(
 | 
						|
                            "%d Anti Aliasing samples selected, but only %d supported by your GPU.",
 | 
						|
                            s_last_multisamples, g_ogl_config.max_samples),
 | 
						|
                        10000);
 | 
						|
      }
 | 
						|
 | 
						|
      g_framebuffer_manager.reset();
 | 
						|
      g_framebuffer_manager =
 | 
						|
          std::make_unique<FramebufferManager>(s_target_width, s_target_height, s_MSAASamples);
 | 
						|
 | 
						|
      PixelShaderManager::SetEfbScaleChanged();
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  // ---------------------------------------------------------------------
 | 
						|
  glEnable(GL_BLEND);
 | 
						|
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
 | 
						|
 | 
						|
  // Reset viewport for drawing text
 | 
						|
  glViewport(0, 0, GLInterface->GetBackBufferWidth(), GLInterface->GetBackBufferHeight());
 | 
						|
 | 
						|
  DrawDebugText();
 | 
						|
 | 
						|
  // Do our OSD callbacks
 | 
						|
  OSD::DoCallbacks(OSD::CallbackType::OnFrame);
 | 
						|
  OSD::DrawMessages();
 | 
						|
 | 
						|
#ifdef ANDROID
 | 
						|
  if (s_surface_needs_change.IsSet())
 | 
						|
  {
 | 
						|
    GLInterface->UpdateHandle(s_new_surface_handle);
 | 
						|
    GLInterface->UpdateSurface();
 | 
						|
    s_new_surface_handle = nullptr;
 | 
						|
    s_surface_needs_change.Clear();
 | 
						|
    s_surface_changed.Set();
 | 
						|
  }
 | 
						|
#endif
 | 
						|
 | 
						|
  // Copy the rendered frame to the real window
 | 
						|
  GLInterface->Swap();
 | 
						|
 | 
						|
  // Clear framebuffer
 | 
						|
  glClearColor(0, 0, 0, 0);
 | 
						|
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
 | 
						|
 | 
						|
  if (s_vsync != g_ActiveConfig.IsVSync())
 | 
						|
  {
 | 
						|
    s_vsync = g_ActiveConfig.IsVSync();
 | 
						|
    if (!DriverDetails::HasBug(DriverDetails::BUG_BROKEN_VSYNC))
 | 
						|
      GLInterface->SwapInterval(s_vsync);
 | 
						|
  }
 | 
						|
 | 
						|
  // Clean out old stuff from caches. It's not worth it to clean out the shader caches.
 | 
						|
  g_texture_cache->Cleanup(frameCount);
 | 
						|
 | 
						|
  // Render to the framebuffer.
 | 
						|
  FramebufferManager::SetFramebuffer(0);
 | 
						|
 | 
						|
  RestoreAPIState();
 | 
						|
 | 
						|
  g_Config.iSaveTargetId = 0;
 | 
						|
 | 
						|
  UpdateActiveConfig();
 | 
						|
  g_texture_cache->OnConfigChanged(g_ActiveConfig);
 | 
						|
 | 
						|
  // For testing zbuffer targets.
 | 
						|
  // Renderer::SetZBufferRender();
 | 
						|
  // SaveTexture("tex.png", GL_TEXTURE_2D, s_FakeZTarget,
 | 
						|
  //	      GetTargetWidth(), GetTargetHeight());
 | 
						|
 | 
						|
  // Invalidate EFB cache
 | 
						|
  ClearEFBCache();
 | 
						|
}
 | 
						|
 | 
						|
void Renderer::DrawFrame(GLuint framebuffer, const TargetRectangle& target_rc,
 | 
						|
                         const EFBRectangle& source_rc, u32 xfb_addr,
 | 
						|
                         const XFBSourceBase* const* xfb_sources, u32 xfb_count, u32 fb_width,
 | 
						|
                         u32 fb_stride, u32 fb_height)
 | 
						|
{
 | 
						|
  if (g_ActiveConfig.bUseXFB)
 | 
						|
  {
 | 
						|
    if (g_ActiveConfig.bUseRealXFB)
 | 
						|
      DrawRealXFB(framebuffer, target_rc, xfb_sources, xfb_count, fb_width, fb_stride, fb_height);
 | 
						|
    else
 | 
						|
      DrawVirtualXFB(framebuffer, target_rc, xfb_addr, xfb_sources, xfb_count, fb_width, fb_stride,
 | 
						|
                     fb_height);
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    DrawEFB(framebuffer, target_rc, source_rc);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void Renderer::DrawEFB(GLuint framebuffer, const TargetRectangle& target_rc,
 | 
						|
                       const EFBRectangle& source_rc)
 | 
						|
{
 | 
						|
  TargetRectangle scaled_source_rc = ConvertEFBRectangle(source_rc);
 | 
						|
 | 
						|
  // for msaa mode, we must resolve the efb content to non-msaa
 | 
						|
  GLuint tex = FramebufferManager::ResolveAndGetRenderTarget(source_rc);
 | 
						|
  glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
 | 
						|
  BlitScreen(scaled_source_rc, target_rc, tex, s_target_width, s_target_height);
 | 
						|
}
 | 
						|
 | 
						|
void Renderer::DrawVirtualXFB(GLuint framebuffer, const TargetRectangle& target_rc, u32 xfb_addr,
 | 
						|
                              const XFBSourceBase* const* xfb_sources, u32 xfb_count, u32 fb_width,
 | 
						|
                              u32 fb_stride, u32 fb_height)
 | 
						|
{
 | 
						|
  glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
 | 
						|
 | 
						|
  for (u32 i = 0; i < xfb_count; ++i)
 | 
						|
  {
 | 
						|
    const XFBSource* xfbSource = static_cast<const XFBSource*>(xfb_sources[i]);
 | 
						|
 | 
						|
    TargetRectangle draw_rc;
 | 
						|
    TargetRectangle source_rc;
 | 
						|
    source_rc.left = xfbSource->sourceRc.left;
 | 
						|
    source_rc.right = xfbSource->sourceRc.right;
 | 
						|
    source_rc.top = xfbSource->sourceRc.top;
 | 
						|
    source_rc.bottom = xfbSource->sourceRc.bottom;
 | 
						|
 | 
						|
    // use virtual xfb with offset
 | 
						|
    int xfbHeight = xfbSource->srcHeight;
 | 
						|
    int xfbWidth = xfbSource->srcWidth;
 | 
						|
    int hOffset = (static_cast<s32>(xfbSource->srcAddr) - static_cast<s32>(xfb_addr)) /
 | 
						|
                  (static_cast<s32>(fb_stride) * 2);
 | 
						|
 | 
						|
    draw_rc.top = target_rc.top - hOffset * target_rc.GetHeight() / static_cast<s32>(fb_height);
 | 
						|
    draw_rc.bottom =
 | 
						|
        target_rc.top - (hOffset + xfbHeight) * target_rc.GetHeight() / static_cast<s32>(fb_height);
 | 
						|
    draw_rc.left =
 | 
						|
        target_rc.left +
 | 
						|
        (target_rc.GetWidth() - xfbWidth * target_rc.GetWidth() / static_cast<s32>(fb_stride)) / 2;
 | 
						|
    draw_rc.right =
 | 
						|
        target_rc.left +
 | 
						|
        (target_rc.GetWidth() + xfbWidth * target_rc.GetWidth() / static_cast<s32>(fb_stride)) / 2;
 | 
						|
 | 
						|
    // The following code disables auto stretch.  Kept for reference.
 | 
						|
    // scale draw area for a 1 to 1 pixel mapping with the draw target
 | 
						|
    // float h_scale = static_cast<float>(fb_width) / static_cast<float>(target_rc.GetWidth());
 | 
						|
    // float v_scale = static_cast<float>(fb_height) / static_cast<float>(target_rc.GetHeight());
 | 
						|
    // draw_rc.top *= v_scale;
 | 
						|
    // draw_rc.bottom *= v_scale;
 | 
						|
    // draw_rc.left *= h_scale;
 | 
						|
    // draw_rc.right *= h_scale;
 | 
						|
 | 
						|
    source_rc.right -= Renderer::EFBToScaledX(fb_stride - fb_width);
 | 
						|
 | 
						|
    BlitScreen(source_rc, draw_rc, xfbSource->texture, xfbSource->texWidth, xfbSource->texHeight);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void Renderer::DrawRealXFB(GLuint framebuffer, const TargetRectangle& target_rc,
 | 
						|
                           const XFBSourceBase* const* xfb_sources, u32 xfb_count, u32 fb_width,
 | 
						|
                           u32 fb_stride, u32 fb_height)
 | 
						|
{
 | 
						|
  glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
 | 
						|
 | 
						|
  for (u32 i = 0; i < xfb_count; ++i)
 | 
						|
  {
 | 
						|
    const XFBSource* xfbSource = static_cast<const XFBSource*>(xfb_sources[i]);
 | 
						|
 | 
						|
    TargetRectangle source_rc;
 | 
						|
    source_rc.left = xfbSource->sourceRc.left;
 | 
						|
    source_rc.right = xfbSource->sourceRc.right;
 | 
						|
    source_rc.top = xfbSource->sourceRc.top;
 | 
						|
    source_rc.bottom = xfbSource->sourceRc.bottom;
 | 
						|
 | 
						|
    source_rc.right -= fb_stride - fb_width;
 | 
						|
 | 
						|
    // RealXFB doesn't call ConvertEFBRectangle for sourceRc, therefore it is still assuming a top-
 | 
						|
    // left origin. The top offset is always zero (see FramebufferManagerBase::GetRealXFBSource).
 | 
						|
    source_rc.top = source_rc.bottom;
 | 
						|
    source_rc.bottom = 0;
 | 
						|
 | 
						|
    TargetRectangle draw_rc = target_rc;
 | 
						|
    BlitScreen(source_rc, draw_rc, xfbSource->texture, xfbSource->texWidth, xfbSource->texHeight);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void Renderer::FlushFrameDump()
 | 
						|
{
 | 
						|
  if (!m_last_frame_exported)
 | 
						|
    return;
 | 
						|
 | 
						|
  FinishFrameData();
 | 
						|
  glBindBuffer(GL_PIXEL_PACK_BUFFER, m_frame_dumping_pbo[0]);
 | 
						|
  m_frame_pbo_is_mapped[0] = true;
 | 
						|
  void* data = glMapBufferRange(
 | 
						|
      GL_PIXEL_PACK_BUFFER, 0, m_last_frame_width[0] * m_last_frame_height[0] * 4, GL_MAP_READ_BIT);
 | 
						|
  DumpFrameData(reinterpret_cast<u8*>(data), m_last_frame_width[0], m_last_frame_height[0],
 | 
						|
                m_last_frame_width[0] * 4, m_last_frame_state, true);
 | 
						|
  glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
 | 
						|
  m_last_frame_exported = false;
 | 
						|
}
 | 
						|
 | 
						|
void Renderer::DumpFrame(const TargetRectangle& flipped_trc, u64 ticks)
 | 
						|
{
 | 
						|
  if (!m_frame_dumping_pbo[0])
 | 
						|
  {
 | 
						|
    glGenBuffers(2, m_frame_dumping_pbo.data());
 | 
						|
    glBindBuffer(GL_PIXEL_PACK_BUFFER, m_frame_dumping_pbo[0]);
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    FlushFrameDump();
 | 
						|
    std::swap(m_frame_dumping_pbo[0], m_frame_dumping_pbo[1]);
 | 
						|
    std::swap(m_frame_pbo_is_mapped[0], m_frame_pbo_is_mapped[1]);
 | 
						|
    std::swap(m_last_frame_width[0], m_last_frame_width[1]);
 | 
						|
    std::swap(m_last_frame_height[0], m_last_frame_height[1]);
 | 
						|
    glBindBuffer(GL_PIXEL_PACK_BUFFER, m_frame_dumping_pbo[0]);
 | 
						|
    if (m_frame_pbo_is_mapped[0])
 | 
						|
      glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
 | 
						|
    m_frame_pbo_is_mapped[0] = false;
 | 
						|
  }
 | 
						|
 | 
						|
  if (flipped_trc.GetWidth() != m_last_frame_width[0] ||
 | 
						|
      flipped_trc.GetHeight() != m_last_frame_height[0])
 | 
						|
  {
 | 
						|
    m_last_frame_width[0] = flipped_trc.GetWidth();
 | 
						|
    m_last_frame_height[0] = flipped_trc.GetHeight();
 | 
						|
    glBufferData(GL_PIXEL_PACK_BUFFER, m_last_frame_width[0] * m_last_frame_height[0] * 4, nullptr,
 | 
						|
                 GL_STREAM_READ);
 | 
						|
  }
 | 
						|
 | 
						|
  m_last_frame_state = AVIDump::FetchState(ticks);
 | 
						|
  m_last_frame_exported = true;
 | 
						|
 | 
						|
  glPixelStorei(GL_PACK_ALIGNMENT, 1);
 | 
						|
  glReadPixels(flipped_trc.left, flipped_trc.bottom, m_last_frame_width[0], m_last_frame_height[0],
 | 
						|
               GL_RGBA, GL_UNSIGNED_BYTE, 0);
 | 
						|
  glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
 | 
						|
}
 | 
						|
 | 
						|
void Renderer::DumpFrameUsingFBO(const EFBRectangle& source_rc, u32 xfb_addr,
 | 
						|
                                 const XFBSourceBase* const* xfb_sources, u32 xfb_count,
 | 
						|
                                 u32 fb_width, u32 fb_stride, u32 fb_height, u64 ticks)
 | 
						|
{
 | 
						|
  // This needs to be converted to the GL bottom-up window coordinate system.
 | 
						|
  TargetRectangle render_rc = CalculateFrameDumpDrawRectangle();
 | 
						|
  std::swap(render_rc.top, render_rc.bottom);
 | 
						|
 | 
						|
  // Ensure the render texture meets the size requirements of the draw area.
 | 
						|
  u32 render_width = static_cast<u32>(render_rc.GetWidth());
 | 
						|
  u32 render_height = static_cast<u32>(render_rc.GetHeight());
 | 
						|
  PrepareFrameDumpRenderTexture(render_width, render_height);
 | 
						|
 | 
						|
  // Ensure the alpha channel of the render texture is blank. The frame dump backend expects
 | 
						|
  // that the alpha is set to 1.0 for all pixels.
 | 
						|
  glBindFramebuffer(GL_FRAMEBUFFER, m_frame_dump_render_framebuffer);
 | 
						|
  glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
 | 
						|
  glClear(GL_COLOR_BUFFER_BIT);
 | 
						|
 | 
						|
  // Render the frame into the frame dump render texture. Disable alpha writes in case the
 | 
						|
  // post-processing shader writes a non-1.0 value.
 | 
						|
  glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE);
 | 
						|
  DrawFrame(m_frame_dump_render_framebuffer, render_rc, source_rc, xfb_addr, xfb_sources, xfb_count,
 | 
						|
            fb_width, fb_stride, fb_height);
 | 
						|
 | 
						|
  // Copy frame to output buffer. This assumes that GL_FRAMEBUFFER has been set.
 | 
						|
  DumpFrame(render_rc, ticks);
 | 
						|
 | 
						|
  // Restore state after drawing. This isn't the game state, it's the state set by ResetAPIState.
 | 
						|
  glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
 | 
						|
  glBindFramebuffer(GL_FRAMEBUFFER, 0);
 | 
						|
}
 | 
						|
 | 
						|
void Renderer::PrepareFrameDumpRenderTexture(u32 width, u32 height)
 | 
						|
{
 | 
						|
  // Ensure framebuffer exists (we lazily allocate it in case frame dumping isn't used).
 | 
						|
  // Or, resize texture if it isn't large enough to accommodate the current frame.
 | 
						|
  if (m_frame_dump_render_texture != 0 && m_frame_dump_render_framebuffer != 0 &&
 | 
						|
      m_frame_dump_render_texture_width >= width && m_frame_dump_render_texture_height >= height)
 | 
						|
  {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  // Recreate texture objects.
 | 
						|
  if (m_frame_dump_render_texture != 0)
 | 
						|
    glDeleteTextures(1, &m_frame_dump_render_texture);
 | 
						|
  if (m_frame_dump_render_framebuffer != 0)
 | 
						|
    glDeleteFramebuffers(1, &m_frame_dump_render_framebuffer);
 | 
						|
 | 
						|
  glGenTextures(1, &m_frame_dump_render_texture);
 | 
						|
  glActiveTexture(GL_TEXTURE9);
 | 
						|
  glBindTexture(GL_TEXTURE_2D, m_frame_dump_render_texture);
 | 
						|
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
 | 
						|
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
 | 
						|
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
 | 
						|
  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
 | 
						|
 | 
						|
  glGenFramebuffers(1, &m_frame_dump_render_framebuffer);
 | 
						|
  FramebufferManager::SetFramebuffer(m_frame_dump_render_framebuffer);
 | 
						|
  FramebufferManager::FramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
 | 
						|
                                         m_frame_dump_render_texture, 0);
 | 
						|
 | 
						|
  m_frame_dump_render_texture_width = width;
 | 
						|
  m_frame_dump_render_texture_height = height;
 | 
						|
  TextureCache::SetStage();
 | 
						|
}
 | 
						|
 | 
						|
void Renderer::DestroyFrameDumpResources()
 | 
						|
{
 | 
						|
  if (m_frame_dump_render_framebuffer)
 | 
						|
    glDeleteFramebuffers(1, &m_frame_dump_render_framebuffer);
 | 
						|
  if (m_frame_dump_render_texture)
 | 
						|
    glDeleteTextures(1, &m_frame_dump_render_texture);
 | 
						|
  if (m_frame_dumping_pbo[0])
 | 
						|
    glDeleteBuffers(2, m_frame_dumping_pbo.data());
 | 
						|
}
 | 
						|
 | 
						|
// ALWAYS call RestoreAPIState for each ResetAPIState call you're doing
 | 
						|
void Renderer::ResetAPIState()
 | 
						|
{
 | 
						|
  // Gets us to a reasonably sane state where it's possible to do things like
 | 
						|
  // image copies with textured quads, etc.
 | 
						|
  glDisable(GL_SCISSOR_TEST);
 | 
						|
  glDisable(GL_DEPTH_TEST);
 | 
						|
  glDisable(GL_CULL_FACE);
 | 
						|
  glDisable(GL_BLEND);
 | 
						|
  if (GLInterface->GetMode() == GLInterfaceMode::MODE_OPENGL)
 | 
						|
    glDisable(GL_COLOR_LOGIC_OP);
 | 
						|
  if (g_ActiveConfig.backend_info.bSupportsDepthClamp)
 | 
						|
  {
 | 
						|
    glDisable(GL_CLIP_DISTANCE0);
 | 
						|
    glDisable(GL_CLIP_DISTANCE1);
 | 
						|
  }
 | 
						|
  glDepthMask(GL_FALSE);
 | 
						|
  glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
 | 
						|
}
 | 
						|
 | 
						|
void Renderer::RestoreAPIState()
 | 
						|
{
 | 
						|
  // Gets us back into a more game-like state.
 | 
						|
  glEnable(GL_SCISSOR_TEST);
 | 
						|
  if (g_ActiveConfig.backend_info.bSupportsDepthClamp)
 | 
						|
  {
 | 
						|
    glEnable(GL_CLIP_DISTANCE0);
 | 
						|
    glEnable(GL_CLIP_DISTANCE1);
 | 
						|
  }
 | 
						|
  SetGenerationMode();
 | 
						|
  BPFunctions::SetScissor();
 | 
						|
  SetDepthMode();
 | 
						|
  SetBlendMode(true);
 | 
						|
  SetViewport();
 | 
						|
 | 
						|
  const VertexManager* const vm = static_cast<VertexManager*>(g_vertex_manager.get());
 | 
						|
  glBindBuffer(GL_ARRAY_BUFFER, vm->m_vertex_buffers);
 | 
						|
  if (vm->m_last_vao)
 | 
						|
    glBindVertexArray(vm->m_last_vao);
 | 
						|
 | 
						|
  TextureCache::SetStage();
 | 
						|
}
 | 
						|
 | 
						|
void Renderer::SetGenerationMode()
 | 
						|
{
 | 
						|
  // none, ccw, cw, ccw
 | 
						|
  if (bpmem.genMode.cullmode > 0)
 | 
						|
  {
 | 
						|
    // TODO: GX_CULL_ALL not supported, yet!
 | 
						|
    glEnable(GL_CULL_FACE);
 | 
						|
    glFrontFace(bpmem.genMode.cullmode == 2 ? GL_CCW : GL_CW);
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    glDisable(GL_CULL_FACE);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void Renderer::SetDepthMode()
 | 
						|
{
 | 
						|
  const GLenum glCmpFuncs[8] = {GL_NEVER,   GL_LESS,     GL_EQUAL,  GL_LEQUAL,
 | 
						|
                                GL_GREATER, GL_NOTEQUAL, GL_GEQUAL, GL_ALWAYS};
 | 
						|
 | 
						|
  if (bpmem.zmode.testenable)
 | 
						|
  {
 | 
						|
    glEnable(GL_DEPTH_TEST);
 | 
						|
    glDepthMask(bpmem.zmode.updateenable ? GL_TRUE : GL_FALSE);
 | 
						|
    glDepthFunc(glCmpFuncs[bpmem.zmode.func]);
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    // if the test is disabled write is disabled too
 | 
						|
    // TODO: When PE performance metrics are being emulated via occlusion queries, we should
 | 
						|
    // (probably?) enable depth test with depth function ALWAYS here
 | 
						|
    glDisable(GL_DEPTH_TEST);
 | 
						|
    glDepthMask(GL_FALSE);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void Renderer::SetSamplerState(int stage, int texindex, bool custom_tex)
 | 
						|
{
 | 
						|
  auto const& tex = bpmem.tex[texindex];
 | 
						|
  auto const& tm0 = tex.texMode0[stage];
 | 
						|
  auto const& tm1 = tex.texMode1[stage];
 | 
						|
 | 
						|
  g_sampler_cache->SetSamplerState((texindex * 4) + stage, tm0, tm1, custom_tex);
 | 
						|
}
 | 
						|
 | 
						|
void Renderer::SetInterlacingMode()
 | 
						|
{
 | 
						|
  // TODO
 | 
						|
}
 | 
						|
}
 | 
						|
 | 
						|
namespace OGL
 | 
						|
{
 | 
						|
u32 Renderer::GetMaxTextureSize()
 | 
						|
{
 | 
						|
  // Right now nvidia seems to do something very weird if we try to cache GL_MAX_TEXTURE_SIZE in
 | 
						|
  // init. This is a workaround that lets
 | 
						|
  // us keep the perf improvement that caching it gives us.
 | 
						|
  if (s_max_texture_size == 0)
 | 
						|
    glGetIntegerv(GL_MAX_TEXTURE_SIZE, &s_max_texture_size);
 | 
						|
  return static_cast<u32>(s_max_texture_size);
 | 
						|
}
 | 
						|
 | 
						|
void Renderer::ChangeSurface(void* new_surface_handle)
 | 
						|
{
 | 
						|
// Win32 polls the window size when redrawing, X11 runs an event loop in another thread.
 | 
						|
// This is only necessary for Android at this point, although handling resizes here
 | 
						|
// would be more efficient than polling.
 | 
						|
#ifdef ANDROID
 | 
						|
  s_new_surface_handle = new_surface_handle;
 | 
						|
  s_surface_needs_change.Set();
 | 
						|
  s_surface_changed.Wait();
 | 
						|
#endif
 | 
						|
}
 | 
						|
}
 |