2015-05-24 04:55:12 +00:00
|
|
|
// Copyright 2009 Dolphin Emulator Project
|
2015-05-17 23:08:10 +00:00
|
|
|
// Licensed under GPLv2+
|
2013-04-18 03:29:41 +00:00
|
|
|
// Refer to the license.txt file included.
|
2009-07-06 02:10:26 +00:00
|
|
|
|
2017-01-23 16:20:20 +00:00
|
|
|
#include "VideoBackends/OGL/FramebufferManager.h"
|
|
|
|
|
2015-12-21 19:11:01 +00:00
|
|
|
#include <memory>
|
2015-12-22 23:06:05 +00:00
|
|
|
#include <vector>
|
2015-12-21 19:11:01 +00:00
|
|
|
|
2015-09-28 15:57:16 +00:00
|
|
|
#include "Common/Common.h"
|
2017-02-01 15:56:13 +00:00
|
|
|
#include "Common/CommonTypes.h"
|
2015-09-18 16:40:00 +00:00
|
|
|
#include "Common/GL/GLInterfaceBase.h"
|
2017-02-01 15:56:13 +00:00
|
|
|
#include "Common/Logging/Log.h"
|
2017-03-09 22:14:47 +00:00
|
|
|
#include "Common/MsgHandler.h"
|
2017-02-01 15:56:13 +00:00
|
|
|
|
2014-02-17 10:18:15 +00:00
|
|
|
#include "Core/HW/Memmap.h"
|
|
|
|
|
|
|
|
#include "VideoBackends/OGL/Render.h"
|
2015-05-28 22:53:07 +00:00
|
|
|
#include "VideoBackends/OGL/SamplerCache.h"
|
2014-02-17 10:18:15 +00:00
|
|
|
#include "VideoBackends/OGL/TextureConverter.h"
|
2018-01-20 14:59:10 +00:00
|
|
|
#include "VideoBackends/OGL/VertexManager.h"
|
2014-02-17 10:18:15 +00:00
|
|
|
|
|
|
|
#include "VideoCommon/OnScreenDisplay.h"
|
|
|
|
#include "VideoCommon/VertexShaderGen.h"
|
2017-01-23 16:20:20 +00:00
|
|
|
#include "VideoCommon/VideoBackendBase.h"
|
2011-01-29 20:16:51 +00:00
|
|
|
|
2018-04-14 19:04:54 +00:00
|
|
|
constexpr const char* GLSL_REINTERPRET_PIXELFMT_VS = R"GLSL(
|
|
|
|
flat out int layer;
|
|
|
|
void main(void) {
|
|
|
|
layer = 0;
|
|
|
|
vec2 rawpos = vec2(gl_VertexID & 1, gl_VertexID & 2);
|
|
|
|
gl_Position = vec4(rawpos* 2.0 - 1.0, 0.0, 1.0);
|
|
|
|
})GLSL";
|
|
|
|
|
|
|
|
constexpr const char* GLSL_SHADER_FS = R"GLSL(
|
|
|
|
#define MULTILAYER %d
|
|
|
|
#define MSAA %d
|
|
|
|
|
|
|
|
#if MSAA
|
|
|
|
|
|
|
|
#if MULTILAYER
|
|
|
|
SAMPLER_BINDING(9) uniform sampler2DMSArray samp9;
|
|
|
|
#else
|
|
|
|
SAMPLER_BINDING(9) uniform sampler2DMS samp9;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#else
|
|
|
|
SAMPLER_BINDING(9) uniform sampler2DArray samp9;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
vec4 sampleEFB(ivec3 pos) {
|
|
|
|
#if MSAA
|
|
|
|
|
|
|
|
#if MULTILAYER
|
|
|
|
return texelFetch(samp9, pos, gl_SampleID);
|
|
|
|
#else
|
|
|
|
return texelFetch(samp9, pos.xy, gl_SampleID);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#else
|
|
|
|
return texelFetch(samp9, pos, 0);
|
|
|
|
#endif
|
|
|
|
})GLSL";
|
|
|
|
|
|
|
|
constexpr const char* GLSL_SAMPLE_EFB_FS = R"GLSL(
|
|
|
|
#define MULTILAYER %d
|
|
|
|
|
|
|
|
#if MULTILAYER
|
|
|
|
SAMPLER_BINDING(9) uniform sampler2DMSArray samp9;
|
|
|
|
#else
|
|
|
|
SAMPLER_BINDING(9) uniform sampler2DMS samp9;
|
|
|
|
#endif
|
|
|
|
vec4 sampleEFB(ivec3 pos) {
|
|
|
|
vec4 color = vec4(0.0, 0.0, 0.0, 0.0);
|
|
|
|
for (int i = 0; i < %d; i++)
|
|
|
|
#if MULTILAYER
|
|
|
|
color += texelFetch(samp9, pos, i);
|
|
|
|
#else
|
|
|
|
color += texelFetch(samp9, pos.xy, i);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return color / %d;
|
|
|
|
})GLSL";
|
|
|
|
|
|
|
|
constexpr const char* GLSL_RGBA6_TO_RGB8_FS = R"GLSL(
|
|
|
|
flat in int layer;
|
|
|
|
out vec4 ocol0;
|
|
|
|
void main() {
|
|
|
|
ivec4 src6 = ivec4(round(sampleEFB(ivec3(gl_FragCoord.xy, layer)) * 63.f));
|
|
|
|
ivec4 dst8;
|
|
|
|
|
|
|
|
dst8.r = (src6.r << 2) | (src6.g >> 4);
|
|
|
|
dst8.g = ((src6.g & 0xF) << 4) | (src6.b >> 2);
|
|
|
|
dst8.b = ((src6.b & 0x3) << 6) | src6.a;
|
|
|
|
dst8.a = 255;
|
|
|
|
|
|
|
|
ocol0 = float4(dst8) / 255.f;
|
|
|
|
})GLSL";
|
|
|
|
|
|
|
|
constexpr const char* GLSL_RGB8_TO_RGBA6_FS = R"GLSL(
|
|
|
|
flat in int layer;
|
|
|
|
out vec4 ocol0;
|
|
|
|
void main() {
|
|
|
|
ivec4 src8 = ivec4(round(sampleEFB(ivec3(gl_FragCoord.xy, layer)) * 255.f));
|
|
|
|
ivec4 dst6;
|
|
|
|
|
|
|
|
dst6.r = src8.r >> 2;
|
|
|
|
dst6.g = ((src8.r & 0x3) << 4) | (src8.g >> 4);
|
|
|
|
dst6.b = ((src8.g & 0xF) << 2) | (src8.b >> 6);
|
|
|
|
dst6.a = src8.b & 0x3F;
|
|
|
|
ocol0 = float4(dst6) / 63.f;
|
|
|
|
})GLSL";
|
|
|
|
|
|
|
|
constexpr const char* GLSL_GS = R"GLSL(
|
|
|
|
layout(triangles) in;
|
|
|
|
layout(triangle_strip, max_vertices = %d) out;
|
|
|
|
flat out int layer;
|
|
|
|
void main() {
|
2018-05-12 14:41:42 +00:00
|
|
|
for (int j = 0; j < %d; ++j) {
|
2018-04-14 19:04:54 +00:00
|
|
|
for (int i = 0; i < 3; ++i) {
|
|
|
|
layer = j;
|
|
|
|
gl_Layer = j;
|
|
|
|
gl_Position = gl_in[i].gl_Position;
|
|
|
|
EmitVertex();
|
|
|
|
}
|
|
|
|
EndPrimitive();
|
|
|
|
}
|
|
|
|
})GLSL";
|
|
|
|
|
|
|
|
constexpr const char* GLSL_EFB_POKE_VERTEX_VS = R"GLSL(
|
|
|
|
in vec2 rawpos;
|
|
|
|
in vec4 rawcolor0; // color
|
|
|
|
in int rawcolor1; // depth
|
|
|
|
out vec4 v_c;
|
|
|
|
out float v_z;
|
|
|
|
void main(void) {
|
|
|
|
gl_Position = vec4(((rawpos + 0.5) / vec2(640.0, 528.0) * 2.0 - 1.0) * vec2(1.0, -1.0), 0.0, 1.0);
|
|
|
|
gl_PointSize = %d.0 / 640.0;
|
|
|
|
|
|
|
|
v_c = rawcolor0.bgra;
|
|
|
|
v_z = float(rawcolor1 & 0xFFFFFF) / 16777216.0;
|
|
|
|
})GLSL";
|
|
|
|
|
|
|
|
constexpr const char* GLSL_EFB_POKE_PIXEL_FS = R"GLSL(
|
|
|
|
in vec4 %s_c;
|
|
|
|
in float %s_z;
|
|
|
|
out vec4 ocol0;
|
|
|
|
void main(void) {
|
|
|
|
ocol0 = %s_c;
|
|
|
|
gl_FragDepth = %s_z;
|
|
|
|
})GLSL";
|
|
|
|
|
2018-05-12 14:21:29 +00:00
|
|
|
constexpr const char* GLSL_EFB_POKE_GEOMETRY_GS = R"GLSL(
|
2018-04-14 19:04:54 +00:00
|
|
|
layout(points) in;
|
|
|
|
layout(points, max_vertices = %d) out;
|
|
|
|
in vec4 v_c[1];
|
|
|
|
in float v_z[1];
|
|
|
|
out vec4 g_c;
|
|
|
|
out float g_z;
|
|
|
|
void main() {
|
|
|
|
for (int j = 0; j < %d; ++j) {
|
|
|
|
gl_Layer = j;
|
|
|
|
gl_Position = gl_in[0].gl_Position;
|
|
|
|
gl_PointSize = %d.0 / 640.0;
|
|
|
|
g_c = v_c[0];
|
|
|
|
g_z = v_z[0];
|
|
|
|
|
|
|
|
EmitVertex();
|
|
|
|
EndPrimitive();
|
|
|
|
}
|
|
|
|
})GLSL";
|
|
|
|
|
2011-01-29 20:16:51 +00:00
|
|
|
namespace OGL
|
|
|
|
{
|
2010-11-14 23:31:53 +00:00
|
|
|
int FramebufferManager::m_targetWidth;
|
|
|
|
int FramebufferManager::m_targetHeight;
|
|
|
|
int FramebufferManager::m_msaaSamples;
|
2017-03-09 23:33:10 +00:00
|
|
|
bool FramebufferManager::m_enable_stencil_buffer;
|
2009-09-03 20:37:35 +00:00
|
|
|
|
2014-05-01 10:45:05 +00:00
|
|
|
GLenum FramebufferManager::m_textureType;
|
2015-12-22 23:06:05 +00:00
|
|
|
std::vector<GLuint> FramebufferManager::m_efbFramebuffer;
|
2014-05-01 10:45:05 +00:00
|
|
|
GLuint FramebufferManager::m_efbColor;
|
|
|
|
GLuint FramebufferManager::m_efbDepth;
|
2016-06-24 08:43:46 +00:00
|
|
|
GLuint FramebufferManager::m_efbColorSwap; // for hot swap when reinterpreting EFB pixel formats
|
2010-11-14 23:31:53 +00:00
|
|
|
|
|
|
|
// Only used in MSAA mode.
|
2015-12-22 23:06:05 +00:00
|
|
|
std::vector<GLuint> FramebufferManager::m_resolvedFramebuffer;
|
2010-11-14 23:31:53 +00:00
|
|
|
GLuint FramebufferManager::m_resolvedColorTexture;
|
|
|
|
GLuint FramebufferManager::m_resolvedDepthTexture;
|
|
|
|
|
2013-07-22 13:41:10 +00:00
|
|
|
// reinterpret pixel format
|
|
|
|
SHADER FramebufferManager::m_pixel_format_shaders[2];
|
|
|
|
|
2015-05-01 15:47:52 +00:00
|
|
|
// EFB pokes
|
2015-05-29 07:55:45 +00:00
|
|
|
GLuint FramebufferManager::m_EfbPokes_VBO;
|
|
|
|
GLuint FramebufferManager::m_EfbPokes_VAO;
|
|
|
|
SHADER FramebufferManager::m_EfbPokes;
|
2013-07-22 13:41:10 +00:00
|
|
|
|
2017-03-09 22:14:47 +00:00
|
|
|
GLuint FramebufferManager::CreateTexture(GLenum texture_type, GLenum internal_format,
|
|
|
|
GLenum pixel_format, GLenum data_type)
|
|
|
|
{
|
|
|
|
GLuint texture;
|
2017-09-09 02:44:25 +00:00
|
|
|
glActiveTexture(GL_TEXTURE9);
|
2017-03-09 22:14:47 +00:00
|
|
|
glGenTextures(1, &texture);
|
|
|
|
glBindTexture(texture_type, texture);
|
|
|
|
if (texture_type == GL_TEXTURE_2D_ARRAY)
|
|
|
|
{
|
|
|
|
glTexParameteri(texture_type, GL_TEXTURE_MAX_LEVEL, 0);
|
|
|
|
glTexImage3D(texture_type, 0, internal_format, m_targetWidth, m_targetHeight, m_EFBLayers, 0,
|
|
|
|
pixel_format, data_type, nullptr);
|
|
|
|
}
|
2017-03-17 22:53:56 +00:00
|
|
|
else if (texture_type == GL_TEXTURE_2D_MULTISAMPLE_ARRAY)
|
2017-03-09 22:14:47 +00:00
|
|
|
{
|
2016-11-27 08:14:53 +00:00
|
|
|
if (g_ogl_config.bSupports3DTextureStorageMultisample)
|
2017-03-09 22:14:47 +00:00
|
|
|
glTexStorage3DMultisample(texture_type, m_msaaSamples, internal_format, m_targetWidth,
|
|
|
|
m_targetHeight, m_EFBLayers, false);
|
|
|
|
else
|
|
|
|
glTexImage3DMultisample(texture_type, m_msaaSamples, internal_format, m_targetWidth,
|
|
|
|
m_targetHeight, m_EFBLayers, false);
|
|
|
|
}
|
2017-03-17 22:53:56 +00:00
|
|
|
else if (texture_type == GL_TEXTURE_2D_MULTISAMPLE)
|
2017-03-09 22:14:47 +00:00
|
|
|
{
|
2016-11-27 08:14:53 +00:00
|
|
|
if (g_ogl_config.bSupports2DTextureStorageMultisample)
|
2017-03-09 22:14:47 +00:00
|
|
|
glTexStorage2DMultisample(texture_type, m_msaaSamples, internal_format, m_targetWidth,
|
|
|
|
m_targetHeight, false);
|
|
|
|
else
|
|
|
|
glTexImage2DMultisample(texture_type, m_msaaSamples, internal_format, m_targetWidth,
|
|
|
|
m_targetHeight, false);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
PanicAlert("Unhandled texture type %d", texture_type);
|
|
|
|
}
|
|
|
|
glBindTexture(texture_type, 0);
|
|
|
|
return texture;
|
|
|
|
}
|
|
|
|
|
2017-03-09 23:33:10 +00:00
|
|
|
void FramebufferManager::BindLayeredTexture(GLuint texture, const std::vector<GLuint>& framebuffers,
|
|
|
|
GLenum attachment, GLenum texture_type)
|
2017-03-09 22:55:31 +00:00
|
|
|
{
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, framebuffers[0]);
|
|
|
|
FramebufferTexture(GL_FRAMEBUFFER, attachment, texture_type, texture, 0);
|
|
|
|
// Bind all the other layers as separate FBOs for blitting.
|
2017-03-09 23:33:10 +00:00
|
|
|
for (unsigned int i = 1; i < m_EFBLayers; i++)
|
|
|
|
{
|
2017-06-27 21:42:42 +00:00
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, framebuffers[i]);
|
2017-03-09 22:55:31 +00:00
|
|
|
glFramebufferTextureLayer(GL_FRAMEBUFFER, attachment, texture, 0, i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-09 23:33:10 +00:00
|
|
|
bool FramebufferManager::HasStencilBuffer()
|
|
|
|
{
|
|
|
|
return m_enable_stencil_buffer;
|
|
|
|
}
|
|
|
|
|
|
|
|
FramebufferManager::FramebufferManager(int targetWidth, int targetHeight, int msaaSamples,
|
|
|
|
bool enable_stencil_buffer)
|
2009-07-06 02:10:26 +00:00
|
|
|
{
|
2016-06-24 08:43:46 +00:00
|
|
|
m_efbColor = 0;
|
|
|
|
m_efbDepth = 0;
|
|
|
|
m_efbColorSwap = 0;
|
|
|
|
m_resolvedColorTexture = 0;
|
|
|
|
m_resolvedDepthTexture = 0;
|
|
|
|
|
|
|
|
m_targetWidth = targetWidth;
|
|
|
|
m_targetHeight = targetHeight;
|
|
|
|
m_msaaSamples = msaaSamples;
|
2017-03-09 23:33:10 +00:00
|
|
|
m_enable_stencil_buffer = enable_stencil_buffer;
|
2016-06-24 08:43:46 +00:00
|
|
|
|
|
|
|
// The EFB can be set to different pixel formats by the game through the
|
|
|
|
// BPMEM_ZCOMPARE register (which should probably have a different name).
|
|
|
|
// They are:
|
|
|
|
// - 24-bit RGB (8-bit components) with 24-bit Z
|
|
|
|
// - 24-bit RGBA (6-bit components) with 24-bit Z
|
|
|
|
// - Multisampled 16-bit RGB (5-6-5 format) with 16-bit Z
|
|
|
|
// We only use one EFB format here: 32-bit ARGB with 24-bit Z.
|
|
|
|
// Multisampling depends on user settings.
|
|
|
|
// The distinction becomes important for certain operations, i.e. the
|
|
|
|
// alpha channel should be ignored if the EFB does not have one.
|
|
|
|
|
|
|
|
glActiveTexture(GL_TEXTURE9);
|
|
|
|
|
2017-11-11 03:55:00 +00:00
|
|
|
m_EFBLayers = (g_ActiveConfig.stereo_mode != StereoMode::Off) ? 2 : 1;
|
2016-06-24 08:43:46 +00:00
|
|
|
m_efbFramebuffer.resize(m_EFBLayers);
|
|
|
|
m_resolvedFramebuffer.resize(m_EFBLayers);
|
|
|
|
|
2017-03-09 23:33:10 +00:00
|
|
|
GLenum depth_internal_format = GL_DEPTH_COMPONENT32F;
|
|
|
|
GLenum depth_pixel_format = GL_DEPTH_COMPONENT;
|
|
|
|
GLenum depth_data_type = GL_FLOAT;
|
|
|
|
if (m_enable_stencil_buffer)
|
|
|
|
{
|
|
|
|
depth_internal_format = GL_DEPTH32F_STENCIL8;
|
|
|
|
depth_pixel_format = GL_DEPTH_STENCIL;
|
|
|
|
depth_data_type = GL_FLOAT_32_UNSIGNED_INT_24_8_REV;
|
|
|
|
}
|
|
|
|
|
2018-04-14 19:04:54 +00:00
|
|
|
const bool multilayer = m_EFBLayers > 1;
|
|
|
|
|
2016-06-24 08:43:46 +00:00
|
|
|
if (m_msaaSamples <= 1)
|
|
|
|
{
|
|
|
|
m_textureType = GL_TEXTURE_2D_ARRAY;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Only use a layered multisample texture if needed. Some drivers
|
|
|
|
// slow down significantly with single-layered multisample textures.
|
2018-04-14 19:04:54 +00:00
|
|
|
m_textureType = multilayer ? GL_TEXTURE_2D_MULTISAMPLE_ARRAY : GL_TEXTURE_2D_MULTISAMPLE;
|
2016-06-24 08:43:46 +00:00
|
|
|
|
2018-04-14 19:04:54 +00:00
|
|
|
// Although we are able to access the multisampled texture directly, we don't do it
|
|
|
|
// everywhere. The old way is to "resolve" this multisampled texture by copying it into a
|
|
|
|
// non-sampled texture. This would lead to an unneeded copy of the EFB, so we are going to
|
|
|
|
// avoid it. But as this job isn't done right now, we do need that texture for resolving:
|
2017-03-09 22:14:47 +00:00
|
|
|
GLenum resolvedType = GL_TEXTURE_2D_ARRAY;
|
2016-06-24 08:43:46 +00:00
|
|
|
|
2017-03-09 22:14:47 +00:00
|
|
|
m_resolvedColorTexture = CreateTexture(resolvedType, GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE);
|
|
|
|
m_resolvedDepthTexture =
|
2017-03-09 23:33:10 +00:00
|
|
|
CreateTexture(resolvedType, depth_internal_format, depth_pixel_format, depth_data_type);
|
2016-06-24 08:43:46 +00:00
|
|
|
|
|
|
|
// Bind resolved textures to resolved framebuffer.
|
|
|
|
glGenFramebuffers(m_EFBLayers, m_resolvedFramebuffer.data());
|
2017-03-09 23:33:10 +00:00
|
|
|
BindLayeredTexture(m_resolvedColorTexture, m_resolvedFramebuffer, GL_COLOR_ATTACHMENT0,
|
|
|
|
resolvedType);
|
|
|
|
BindLayeredTexture(m_resolvedDepthTexture, m_resolvedFramebuffer, GL_DEPTH_ATTACHMENT,
|
|
|
|
resolvedType);
|
|
|
|
if (m_enable_stencil_buffer)
|
|
|
|
BindLayeredTexture(m_resolvedDepthTexture, m_resolvedFramebuffer, GL_STENCIL_ATTACHMENT,
|
|
|
|
resolvedType);
|
2016-06-24 08:43:46 +00:00
|
|
|
}
|
|
|
|
|
2017-03-09 22:14:47 +00:00
|
|
|
m_efbColor = CreateTexture(m_textureType, GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE);
|
2017-03-09 23:33:10 +00:00
|
|
|
m_efbDepth =
|
|
|
|
CreateTexture(m_textureType, depth_internal_format, depth_pixel_format, depth_data_type);
|
2017-03-09 22:14:47 +00:00
|
|
|
m_efbColorSwap = CreateTexture(m_textureType, GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE);
|
|
|
|
|
2016-06-24 08:43:46 +00:00
|
|
|
// Bind target textures to EFB framebuffer.
|
|
|
|
glGenFramebuffers(m_EFBLayers, m_efbFramebuffer.data());
|
2017-03-09 22:55:31 +00:00
|
|
|
BindLayeredTexture(m_efbColor, m_efbFramebuffer, GL_COLOR_ATTACHMENT0, m_textureType);
|
|
|
|
BindLayeredTexture(m_efbDepth, m_efbFramebuffer, GL_DEPTH_ATTACHMENT, m_textureType);
|
2017-03-09 23:33:10 +00:00
|
|
|
if (m_enable_stencil_buffer)
|
|
|
|
BindLayeredTexture(m_efbDepth, m_efbFramebuffer, GL_STENCIL_ATTACHMENT, m_textureType);
|
2016-06-24 08:43:46 +00:00
|
|
|
|
2016-12-27 03:05:17 +00:00
|
|
|
// EFB framebuffer is currently bound, make sure to clear it before use.
|
2016-06-24 08:43:46 +00:00
|
|
|
glViewport(0, 0, m_targetWidth, m_targetHeight);
|
|
|
|
glScissor(0, 0, m_targetWidth, m_targetHeight);
|
2016-12-27 03:05:17 +00:00
|
|
|
glClearColor(0.f, 0.f, 0.f, 0.f);
|
2016-06-24 08:43:46 +00:00
|
|
|
glClearDepthf(1.0f);
|
2017-03-09 23:33:10 +00:00
|
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
|
|
if (m_enable_stencil_buffer)
|
|
|
|
{
|
|
|
|
glClearStencil(0);
|
|
|
|
glClear(GL_STENCIL_BUFFER_BIT);
|
|
|
|
}
|
2016-06-24 08:43:46 +00:00
|
|
|
|
|
|
|
// reinterpret pixel format
|
2018-05-07 14:11:27 +00:00
|
|
|
std::string vs = GLSL_REINTERPRET_PIXELFMT_VS;
|
2016-06-24 08:43:46 +00:00
|
|
|
|
|
|
|
// The way to sample the EFB is based on the on the current configuration.
|
|
|
|
// As we use the same sampling way for both interpreting shaders, the sampling
|
|
|
|
// shader are generated first:
|
|
|
|
std::string sampler;
|
2018-04-14 19:04:54 +00:00
|
|
|
|
2016-06-24 08:43:46 +00:00
|
|
|
if (m_msaaSamples <= 1)
|
|
|
|
{
|
|
|
|
// non-msaa, so just fetch the pixel
|
2018-04-14 19:04:54 +00:00
|
|
|
sampler = StringFromFormat(GLSL_SHADER_FS, multilayer, false);
|
2016-06-24 08:43:46 +00:00
|
|
|
}
|
|
|
|
else if (g_ActiveConfig.backend_info.bSupportsSSAA)
|
|
|
|
{
|
|
|
|
// msaa + sample shading available, so just fetch the sample
|
|
|
|
// This will lead to sample shading, but it's the only way to not loose
|
|
|
|
// the values of each sample.
|
2018-04-14 19:04:54 +00:00
|
|
|
sampler = StringFromFormat(GLSL_SHADER_FS, multilayer, true);
|
2016-06-24 08:43:46 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// msaa without sample shading: calculate the mean value of the pixel
|
2018-04-14 19:04:54 +00:00
|
|
|
sampler = StringFromFormat(GLSL_SAMPLE_EFB_FS, multilayer, m_msaaSamples, m_msaaSamples);
|
2016-06-24 08:43:46 +00:00
|
|
|
}
|
|
|
|
|
2018-04-14 19:04:54 +00:00
|
|
|
std::string ps_rgba6_to_rgb8 = sampler + GLSL_RGBA6_TO_RGB8_FS;
|
|
|
|
|
|
|
|
std::string ps_rgb8_to_rgba6 = sampler + GLSL_RGB8_TO_RGBA6_FS;
|
|
|
|
|
|
|
|
std::string gs = StringFromFormat(GLSL_GS, m_EFBLayers * 3, m_EFBLayers);
|
2016-06-24 08:43:46 +00:00
|
|
|
|
2016-09-09 10:26:52 +00:00
|
|
|
ProgramShaderCache::CompileShader(m_pixel_format_shaders[0], vs, ps_rgb8_to_rgba6,
|
2018-04-14 19:04:54 +00:00
|
|
|
multilayer ? gs : "");
|
2016-09-09 10:26:52 +00:00
|
|
|
ProgramShaderCache::CompileShader(m_pixel_format_shaders[1], vs, ps_rgba6_to_rgb8,
|
2018-04-14 19:04:54 +00:00
|
|
|
multilayer ? gs : "");
|
|
|
|
|
|
|
|
const auto prefix = multilayer ? "g" : "v";
|
2016-06-24 08:43:46 +00:00
|
|
|
|
|
|
|
ProgramShaderCache::CompileShader(
|
2018-04-14 19:04:54 +00:00
|
|
|
m_EfbPokes, StringFromFormat(GLSL_EFB_POKE_VERTEX_VS, m_targetWidth),
|
|
|
|
|
|
|
|
StringFromFormat(GLSL_EFB_POKE_PIXEL_FS, prefix, prefix, prefix, prefix),
|
|
|
|
|
|
|
|
multilayer ?
|
|
|
|
StringFromFormat(GLSL_EFB_POKE_GEOMETRY_GS, m_EFBLayers, m_EFBLayers, m_targetWidth) :
|
|
|
|
"");
|
2016-06-24 08:43:46 +00:00
|
|
|
glGenBuffers(1, &m_EfbPokes_VBO);
|
|
|
|
glGenVertexArrays(1, &m_EfbPokes_VAO);
|
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, m_EfbPokes_VBO);
|
|
|
|
glBindVertexArray(m_EfbPokes_VAO);
|
|
|
|
glEnableVertexAttribArray(SHADER_POSITION_ATTRIB);
|
|
|
|
glVertexAttribPointer(SHADER_POSITION_ATTRIB, 2, GL_UNSIGNED_SHORT, 0, sizeof(EfbPokeData),
|
|
|
|
(void*)offsetof(EfbPokeData, x));
|
|
|
|
glEnableVertexAttribArray(SHADER_COLOR0_ATTRIB);
|
|
|
|
glVertexAttribPointer(SHADER_COLOR0_ATTRIB, 4, GL_UNSIGNED_BYTE, 1, sizeof(EfbPokeData),
|
|
|
|
(void*)offsetof(EfbPokeData, data));
|
|
|
|
glEnableVertexAttribArray(SHADER_COLOR1_ATTRIB);
|
|
|
|
glVertexAttribIPointer(SHADER_COLOR1_ATTRIB, 1, GL_INT, sizeof(EfbPokeData),
|
|
|
|
(void*)offsetof(EfbPokeData, data));
|
2018-01-20 14:59:10 +00:00
|
|
|
glBindBuffer(GL_ARRAY_BUFFER,
|
|
|
|
static_cast<VertexManager*>(g_vertex_manager.get())->GetVertexBufferHandle());
|
2016-06-24 08:43:46 +00:00
|
|
|
|
|
|
|
if (GLInterface->GetMode() == GLInterfaceMode::MODE_OPENGL)
|
|
|
|
glEnable(GL_PROGRAM_POINT_SIZE);
|
2009-07-06 02:10:26 +00:00
|
|
|
}
|
|
|
|
|
2010-11-14 23:31:53 +00:00
|
|
|
FramebufferManager::~FramebufferManager()
|
2009-07-06 02:10:26 +00:00
|
|
|
{
|
2016-06-24 08:43:46 +00:00
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
|
|
|
|
|
|
|
GLuint glObj[3];
|
|
|
|
|
|
|
|
// Note: OpenGL deletion functions silently ignore parameters of "0".
|
|
|
|
|
|
|
|
glDeleteFramebuffers(m_EFBLayers, m_efbFramebuffer.data());
|
|
|
|
glDeleteFramebuffers(m_EFBLayers, m_resolvedFramebuffer.data());
|
|
|
|
|
|
|
|
// Required, as these are static class members
|
|
|
|
m_efbFramebuffer.clear();
|
|
|
|
m_resolvedFramebuffer.clear();
|
|
|
|
|
|
|
|
glObj[0] = m_resolvedColorTexture;
|
|
|
|
glObj[1] = m_resolvedDepthTexture;
|
|
|
|
glDeleteTextures(2, glObj);
|
|
|
|
m_resolvedColorTexture = 0;
|
|
|
|
m_resolvedDepthTexture = 0;
|
|
|
|
|
|
|
|
glObj[0] = m_efbColor;
|
|
|
|
glObj[1] = m_efbDepth;
|
|
|
|
glObj[2] = m_efbColorSwap;
|
|
|
|
glDeleteTextures(3, glObj);
|
|
|
|
m_efbColor = 0;
|
|
|
|
m_efbDepth = 0;
|
|
|
|
m_efbColorSwap = 0;
|
|
|
|
|
|
|
|
// reinterpret pixel format
|
|
|
|
m_pixel_format_shaders[0].Destroy();
|
|
|
|
m_pixel_format_shaders[1].Destroy();
|
|
|
|
|
|
|
|
// EFB pokes
|
|
|
|
glDeleteBuffers(1, &m_EfbPokes_VBO);
|
|
|
|
glDeleteVertexArrays(1, &m_EfbPokes_VAO);
|
|
|
|
m_EfbPokes_VBO = 0;
|
|
|
|
m_EfbPokes_VAO = 0;
|
|
|
|
m_EfbPokes.Destroy();
|
2009-07-06 02:10:26 +00:00
|
|
|
}
|
|
|
|
|
2010-11-14 23:31:53 +00:00
|
|
|
GLuint FramebufferManager::GetEFBColorTexture(const EFBRectangle& sourceRc)
|
2009-07-06 02:10:26 +00:00
|
|
|
{
|
2016-06-24 08:43:46 +00:00
|
|
|
if (m_msaaSamples <= 1)
|
|
|
|
{
|
|
|
|
return m_efbColor;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Transfer the EFB to a resolved texture. EXT_framebuffer_blit is
|
|
|
|
// required.
|
|
|
|
|
|
|
|
TargetRectangle targetRc = g_renderer->ConvertEFBRectangle(sourceRc);
|
|
|
|
targetRc.ClampUL(0, 0, m_targetWidth, m_targetHeight);
|
|
|
|
|
|
|
|
// Resolve.
|
|
|
|
for (unsigned int i = 0; i < m_EFBLayers; i++)
|
|
|
|
{
|
|
|
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, m_efbFramebuffer[i]);
|
|
|
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_resolvedFramebuffer[i]);
|
|
|
|
glBlitFramebuffer(targetRc.left, targetRc.top, targetRc.right, targetRc.bottom, targetRc.left,
|
|
|
|
targetRc.top, targetRc.right, targetRc.bottom, GL_COLOR_BUFFER_BIT,
|
|
|
|
GL_NEAREST);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Return to EFB.
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, m_efbFramebuffer[0]);
|
|
|
|
|
|
|
|
return m_resolvedColorTexture;
|
|
|
|
}
|
2009-07-06 02:10:26 +00:00
|
|
|
}
|
|
|
|
|
2010-11-14 23:31:53 +00:00
|
|
|
GLuint FramebufferManager::GetEFBDepthTexture(const EFBRectangle& sourceRc)
|
2009-07-06 02:10:26 +00:00
|
|
|
{
|
2016-06-24 08:43:46 +00:00
|
|
|
if (m_msaaSamples <= 1)
|
|
|
|
{
|
|
|
|
return m_efbDepth;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Transfer the EFB to a resolved texture.
|
|
|
|
|
|
|
|
TargetRectangle targetRc = g_renderer->ConvertEFBRectangle(sourceRc);
|
|
|
|
targetRc.ClampUL(0, 0, m_targetWidth, m_targetHeight);
|
|
|
|
|
|
|
|
// Resolve.
|
|
|
|
for (unsigned int i = 0; i < m_EFBLayers; i++)
|
|
|
|
{
|
|
|
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, m_efbFramebuffer[i]);
|
|
|
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_resolvedFramebuffer[i]);
|
|
|
|
glBlitFramebuffer(targetRc.left, targetRc.top, targetRc.right, targetRc.bottom, targetRc.left,
|
|
|
|
targetRc.top, targetRc.right, targetRc.bottom, GL_DEPTH_BUFFER_BIT,
|
|
|
|
GL_NEAREST);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Return to EFB.
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, m_efbFramebuffer[0]);
|
|
|
|
|
|
|
|
return m_resolvedDepthTexture;
|
|
|
|
}
|
2009-07-06 02:10:26 +00:00
|
|
|
}
|
|
|
|
|
OGL: implement Bounding Box on systems w/o SSBO
This commit should have zero performance effect if SSBOs are supported.
If they aren't (e.g. on all Macs), this commit alters FramebufferManager
to attach a new stencil buffer and VertexManager to draw to it when
bounding box is active. `BBoxRead` gets the pixel data from the buffer
and dumbly loops through it to find the bounding box.
This patch can run Paper Mario: The Thousand-Year Door at almost full
speed (50–60 FPS) without Dual-Core enabled for all common bounding
box-using actions I tested (going through pipes, Plane Mode, Paper
Mode, Prof. Frankly's gate, combat, walking around the overworld, etc.)
on my computer (macOS 10.12.3, 2.8 GHz Intel Core i7, 16 GB 1600 MHz
DDR3, and Intel Iris 1536 MB).
A few more demanding scenes (e.g. the self-building bridge on the way
to Petalburg) slow to ~15% of their speed without this patch (though
they don't run quite at full speed even on master). The slowdown is
caused almost solely by `glReadPixels` in `OGL::BoundingBox::Get`.
Other implementation ideas:
- Use a stencil buffer that's separate from the depth buffer. This would
require ARB_texture_stencil8 / OpenGL 4.4, which isn't available on
macOS.
- Use `glGetTexImage` instead of `glReadPixels`. This is ~5 FPS slower
on my computer, presumably because it has to transfer the entire
combined depth-stencil buffer instead of only the stencil data.
Getting only stencil data from `glGetTexImage` requires
ARB_texture_stencil8 / OpenGL 4.4, which (again) is not available on
macOS.
- Don't use a PBO, and use `glReadPixels` synchronously. This has no
visible performance effect on my computer, and is theoretically
slower.
2017-03-05 23:34:30 +00:00
|
|
|
void FramebufferManager::ResolveEFBStencilTexture()
|
|
|
|
{
|
|
|
|
if (m_msaaSamples <= 1)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Resolve.
|
|
|
|
for (unsigned int i = 0; i < m_EFBLayers; i++)
|
|
|
|
{
|
|
|
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, m_efbFramebuffer[i]);
|
|
|
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_resolvedFramebuffer[i]);
|
|
|
|
glBlitFramebuffer(0, 0, m_targetWidth, m_targetHeight, 0, 0, m_targetWidth, m_targetHeight,
|
|
|
|
GL_STENCIL_BUFFER_BIT, GL_NEAREST);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Return to EFB.
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, m_efbFramebuffer[0]);
|
|
|
|
}
|
|
|
|
|
|
|
|
GLuint FramebufferManager::GetResolvedFramebuffer()
|
|
|
|
{
|
|
|
|
if (m_msaaSamples <= 1)
|
|
|
|
return m_efbFramebuffer[0];
|
|
|
|
return m_resolvedFramebuffer[0];
|
|
|
|
}
|
|
|
|
|
2010-11-14 23:31:53 +00:00
|
|
|
void FramebufferManager::SetFramebuffer(GLuint fb)
|
2009-07-06 02:10:26 +00:00
|
|
|
{
|
2016-06-24 08:43:46 +00:00
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, fb != 0 ? fb : GetEFBFramebuffer());
|
2010-11-14 23:31:53 +00:00
|
|
|
}
|
2009-07-06 02:10:26 +00:00
|
|
|
|
2016-06-24 08:43:46 +00:00
|
|
|
void FramebufferManager::FramebufferTexture(GLenum target, GLenum attachment, GLenum textarget,
|
|
|
|
GLuint texture, GLint level)
|
2014-12-04 13:59:16 +00:00
|
|
|
{
|
2016-06-24 08:43:46 +00:00
|
|
|
if (textarget == GL_TEXTURE_2D_ARRAY || textarget == GL_TEXTURE_2D_MULTISAMPLE_ARRAY)
|
|
|
|
{
|
|
|
|
if (m_EFBLayers > 1)
|
|
|
|
glFramebufferTexture(target, attachment, texture, level);
|
|
|
|
else
|
|
|
|
glFramebufferTextureLayer(target, attachment, texture, level, 0);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
glFramebufferTexture2D(target, attachment, textarget, texture, level);
|
|
|
|
}
|
2014-12-04 13:59:16 +00:00
|
|
|
}
|
|
|
|
|
2010-11-14 23:31:53 +00:00
|
|
|
// Apply AA if enabled
|
2016-06-24 08:43:46 +00:00
|
|
|
GLuint FramebufferManager::ResolveAndGetRenderTarget(const EFBRectangle& source_rect)
|
2010-11-14 23:31:53 +00:00
|
|
|
{
|
2016-06-24 08:43:46 +00:00
|
|
|
return GetEFBColorTexture(source_rect);
|
2010-11-14 23:31:53 +00:00
|
|
|
}
|
2009-07-06 02:10:26 +00:00
|
|
|
|
2016-06-24 08:43:46 +00:00
|
|
|
GLuint FramebufferManager::ResolveAndGetDepthTarget(const EFBRectangle& source_rect)
|
2010-11-14 23:31:53 +00:00
|
|
|
{
|
2016-06-24 08:43:46 +00:00
|
|
|
return GetEFBDepthTexture(source_rect);
|
2010-11-14 23:31:53 +00:00
|
|
|
}
|
2009-07-06 02:10:26 +00:00
|
|
|
|
2013-07-22 13:41:10 +00:00
|
|
|
void FramebufferManager::ReinterpretPixelData(unsigned int convtype)
|
|
|
|
{
|
2016-06-24 08:43:46 +00:00
|
|
|
g_renderer->ResetAPIState();
|
2013-10-29 05:23:17 +00:00
|
|
|
|
2016-06-24 08:43:46 +00:00
|
|
|
GLuint src_texture = 0;
|
2013-10-29 05:23:17 +00:00
|
|
|
|
2016-06-24 08:43:46 +00:00
|
|
|
// We aren't allowed to render and sample the same texture in one draw call,
|
|
|
|
// so we have to create a new texture and overwrite it completely.
|
|
|
|
// To not allocate one big texture every time, we've allocated two on
|
|
|
|
// initialization and just swap them here:
|
|
|
|
src_texture = m_efbColor;
|
|
|
|
m_efbColor = m_efbColorSwap;
|
|
|
|
m_efbColorSwap = src_texture;
|
|
|
|
FramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_textureType, m_efbColor, 0);
|
2013-10-29 05:23:17 +00:00
|
|
|
|
2016-06-24 08:43:46 +00:00
|
|
|
glViewport(0, 0, m_targetWidth, m_targetHeight);
|
|
|
|
glActiveTexture(GL_TEXTURE9);
|
|
|
|
glBindTexture(m_textureType, src_texture);
|
|
|
|
g_sampler_cache->BindNearestSampler(9);
|
2013-10-29 05:23:17 +00:00
|
|
|
|
2016-06-24 08:43:46 +00:00
|
|
|
m_pixel_format_shaders[convtype ? 1 : 0].Bind();
|
2018-01-20 14:59:10 +00:00
|
|
|
ProgramShaderCache::BindVertexFormat(nullptr);
|
2016-06-24 08:43:46 +00:00
|
|
|
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
|
|
|
glBindTexture(m_textureType, 0);
|
2013-10-29 05:23:17 +00:00
|
|
|
|
2016-06-24 08:43:46 +00:00
|
|
|
g_renderer->RestoreAPIState();
|
2013-07-22 13:41:10 +00:00
|
|
|
}
|
|
|
|
|
2015-12-19 14:34:56 +00:00
|
|
|
void FramebufferManager::PokeEFB(EFBAccessType type, const EfbPokeData* points, size_t num_points)
|
2015-05-01 15:47:52 +00:00
|
|
|
{
|
2016-06-24 08:43:46 +00:00
|
|
|
g_renderer->ResetAPIState();
|
|
|
|
|
2017-01-23 07:51:46 +00:00
|
|
|
if (type == EFBAccessType::PokeZ)
|
2016-06-24 08:43:46 +00:00
|
|
|
{
|
|
|
|
glDepthMask(GL_TRUE);
|
|
|
|
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
|
|
|
|
glEnable(GL_DEPTH_TEST);
|
|
|
|
glDepthFunc(GL_ALWAYS);
|
|
|
|
}
|
|
|
|
|
|
|
|
glBindVertexArray(m_EfbPokes_VAO);
|
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, m_EfbPokes_VBO);
|
|
|
|
glBufferData(GL_ARRAY_BUFFER, sizeof(EfbPokeData) * num_points, points, GL_STREAM_DRAW);
|
|
|
|
m_EfbPokes.Bind();
|
|
|
|
glViewport(0, 0, m_targetWidth, m_targetHeight);
|
|
|
|
glDrawArrays(GL_POINTS, 0, (GLsizei)num_points);
|
|
|
|
|
2018-01-20 14:59:10 +00:00
|
|
|
glBindBuffer(GL_ARRAY_BUFFER,
|
|
|
|
static_cast<VertexManager*>(g_vertex_manager.get())->GetVertexBufferHandle());
|
2016-06-24 08:43:46 +00:00
|
|
|
g_renderer->RestoreAPIState();
|
|
|
|
|
|
|
|
// TODO: Could just update the EFB cache with the new value
|
|
|
|
ClearEFBCache();
|
2015-05-01 15:47:52 +00:00
|
|
|
}
|
|
|
|
|
2011-01-29 22:48:33 +00:00
|
|
|
} // namespace OGL
|