mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-07-30 01:29:42 -06:00
Move most backend functionality to VideoCommon
This commit is contained in:
@ -2,175 +2,77 @@
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
|
||||
#include "Common/GL/GLUtil.h"
|
||||
|
||||
#include "VideoBackends/OGL/BoundingBox.h"
|
||||
#include "VideoBackends/OGL/FramebufferManager.h"
|
||||
#include "VideoBackends/OGL/Render.h"
|
||||
|
||||
#include "VideoCommon/DriverDetails.h"
|
||||
#include "VideoCommon/VideoConfig.h"
|
||||
|
||||
static GLuint s_bbox_buffer_id;
|
||||
static GLuint s_pbo;
|
||||
|
||||
static std::array<int, 4> s_stencil_bounds;
|
||||
static bool s_stencil_updated;
|
||||
static bool s_stencil_cleared;
|
||||
|
||||
static int s_target_width;
|
||||
static int s_target_height;
|
||||
|
||||
namespace OGL
|
||||
{
|
||||
void BoundingBox::SetTargetSizeChanged(int target_width, int target_height)
|
||||
void BoundingBox::Init()
|
||||
{
|
||||
if (g_ActiveConfig.BBoxUseFragmentShaderImplementation())
|
||||
if (!g_ActiveConfig.backend_info.bSupportsBBox)
|
||||
return;
|
||||
|
||||
s_target_width = target_width;
|
||||
s_target_height = target_height;
|
||||
s_stencil_updated = false;
|
||||
|
||||
glBindBuffer(GL_PIXEL_PACK_BUFFER, s_pbo);
|
||||
glBufferData(GL_PIXEL_PACK_BUFFER, s_target_width * s_target_height, nullptr, GL_STREAM_READ);
|
||||
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
|
||||
}
|
||||
|
||||
void BoundingBox::Init(int target_width, int target_height)
|
||||
{
|
||||
if (g_ActiveConfig.BBoxUseFragmentShaderImplementation())
|
||||
{
|
||||
int initial_values[4] = {0, 0, 0, 0};
|
||||
glGenBuffers(1, &s_bbox_buffer_id);
|
||||
glBindBuffer(GL_SHADER_STORAGE_BUFFER, s_bbox_buffer_id);
|
||||
glBufferData(GL_SHADER_STORAGE_BUFFER, 4 * sizeof(s32), initial_values, GL_DYNAMIC_DRAW);
|
||||
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, s_bbox_buffer_id);
|
||||
}
|
||||
else
|
||||
{
|
||||
s_stencil_bounds = {{0, 0, 0, 0}};
|
||||
glGenBuffers(1, &s_pbo);
|
||||
SetTargetSizeChanged(target_width, target_height);
|
||||
}
|
||||
int initial_values[4] = {0, 0, 0, 0};
|
||||
glGenBuffers(1, &s_bbox_buffer_id);
|
||||
glBindBuffer(GL_SHADER_STORAGE_BUFFER, s_bbox_buffer_id);
|
||||
glBufferData(GL_SHADER_STORAGE_BUFFER, 4 * sizeof(s32), initial_values, GL_DYNAMIC_DRAW);
|
||||
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, s_bbox_buffer_id);
|
||||
}
|
||||
|
||||
void BoundingBox::Shutdown()
|
||||
{
|
||||
if (g_ActiveConfig.BBoxUseFragmentShaderImplementation())
|
||||
{
|
||||
glDeleteBuffers(1, &s_bbox_buffer_id);
|
||||
}
|
||||
else
|
||||
{
|
||||
glDeleteBuffers(1, &s_pbo);
|
||||
}
|
||||
if (!g_ActiveConfig.backend_info.bSupportsBBox)
|
||||
return;
|
||||
|
||||
glDeleteBuffers(1, &s_bbox_buffer_id);
|
||||
}
|
||||
|
||||
void BoundingBox::Set(int index, int value)
|
||||
{
|
||||
if (g_ActiveConfig.BBoxUseFragmentShaderImplementation())
|
||||
{
|
||||
glBindBuffer(GL_SHADER_STORAGE_BUFFER, s_bbox_buffer_id);
|
||||
glBufferSubData(GL_SHADER_STORAGE_BUFFER, index * sizeof(int), sizeof(int), &value);
|
||||
glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
s_stencil_bounds[index] = value;
|
||||
if (!g_ActiveConfig.backend_info.bSupportsBBox)
|
||||
return;
|
||||
|
||||
if (!s_stencil_cleared)
|
||||
{
|
||||
// Assumes that the EFB framebuffer is currently bound
|
||||
glClearStencil(0);
|
||||
glClear(GL_STENCIL_BUFFER_BIT);
|
||||
s_stencil_updated = false;
|
||||
s_stencil_cleared = true;
|
||||
}
|
||||
}
|
||||
glBindBuffer(GL_SHADER_STORAGE_BUFFER, s_bbox_buffer_id);
|
||||
glBufferSubData(GL_SHADER_STORAGE_BUFFER, index * sizeof(int), sizeof(int), &value);
|
||||
glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
|
||||
}
|
||||
|
||||
int BoundingBox::Get(int index)
|
||||
{
|
||||
if (g_ActiveConfig.BBoxUseFragmentShaderImplementation())
|
||||
if (!g_ActiveConfig.backend_info.bSupportsBBox)
|
||||
return 0;
|
||||
|
||||
int data = 0;
|
||||
glBindBuffer(GL_SHADER_STORAGE_BUFFER, s_bbox_buffer_id);
|
||||
if (!DriverDetails::HasBug(DriverDetails::BUG_SLOW_GETBUFFERSUBDATA) &&
|
||||
!static_cast<Renderer*>(g_renderer.get())->IsGLES())
|
||||
{
|
||||
int data = 0;
|
||||
glBindBuffer(GL_SHADER_STORAGE_BUFFER, s_bbox_buffer_id);
|
||||
if (!DriverDetails::HasBug(DriverDetails::BUG_SLOW_GETBUFFERSUBDATA) &&
|
||||
!static_cast<Renderer*>(g_renderer.get())->IsGLES())
|
||||
{
|
||||
// Using glMapBufferRange to read back the contents of the SSBO is extremely slow
|
||||
// on nVidia drivers. This is more noticeable at higher internal resolutions.
|
||||
// Using glGetBufferSubData instead does not seem to exhibit this slowdown.
|
||||
glGetBufferSubData(GL_SHADER_STORAGE_BUFFER, index * sizeof(int), sizeof(int), &data);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Using glMapBufferRange is faster on AMD cards by a measurable margin.
|
||||
void* ptr = glMapBufferRange(GL_SHADER_STORAGE_BUFFER, index * sizeof(int), sizeof(int),
|
||||
GL_MAP_READ_BIT);
|
||||
if (ptr)
|
||||
{
|
||||
memcpy(&data, ptr, sizeof(int));
|
||||
glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);
|
||||
}
|
||||
}
|
||||
glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
|
||||
return data;
|
||||
// Using glMapBufferRange to read back the contents of the SSBO is extremely slow
|
||||
// on nVidia drivers. This is more noticeable at higher internal resolutions.
|
||||
// Using glGetBufferSubData instead does not seem to exhibit this slowdown.
|
||||
glGetBufferSubData(GL_SHADER_STORAGE_BUFFER, index * sizeof(int), sizeof(int), &data);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (s_stencil_updated)
|
||||
// Using glMapBufferRange is faster on AMD cards by a measurable margin.
|
||||
void* ptr = glMapBufferRange(GL_SHADER_STORAGE_BUFFER, index * sizeof(int), sizeof(int),
|
||||
GL_MAP_READ_BIT);
|
||||
if (ptr)
|
||||
{
|
||||
s_stencil_updated = false;
|
||||
|
||||
FramebufferManager::ResolveEFBStencilTexture();
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, FramebufferManager::GetResolvedFramebuffer());
|
||||
glBindBuffer(GL_PIXEL_PACK_BUFFER, s_pbo);
|
||||
glPixelStorei(GL_PACK_ALIGNMENT, 1);
|
||||
glReadPixels(0, 0, s_target_width, s_target_height, GL_STENCIL_INDEX, GL_UNSIGNED_BYTE, 0);
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, FramebufferManager::GetEFBFramebuffer());
|
||||
|
||||
// Eke every bit of performance out of the compiler that we can
|
||||
std::array<int, 4> bounds = s_stencil_bounds;
|
||||
|
||||
u8* data = static_cast<u8*>(glMapBufferRange(
|
||||
GL_PIXEL_PACK_BUFFER, 0, s_target_height * s_target_width, GL_MAP_READ_BIT));
|
||||
|
||||
for (int row = 0; row < s_target_height; row++)
|
||||
{
|
||||
for (int col = 0; col < s_target_width; col++)
|
||||
{
|
||||
if (data[row * s_target_width + col] == 0)
|
||||
continue;
|
||||
bounds[0] = std::min(bounds[0], col);
|
||||
bounds[1] = std::max(bounds[1], col);
|
||||
bounds[2] = std::min(bounds[2], row);
|
||||
bounds[3] = std::max(bounds[3], row);
|
||||
}
|
||||
}
|
||||
|
||||
s_stencil_bounds = bounds;
|
||||
|
||||
glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
|
||||
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
|
||||
memcpy(&data, ptr, sizeof(int));
|
||||
glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);
|
||||
}
|
||||
|
||||
return s_stencil_bounds[index];
|
||||
}
|
||||
glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
|
||||
return data;
|
||||
}
|
||||
|
||||
void BoundingBox::StencilWasUpdated()
|
||||
{
|
||||
s_stencil_updated = true;
|
||||
s_stencil_cleared = false;
|
||||
}
|
||||
|
||||
bool BoundingBox::NeedsStencilBuffer()
|
||||
{
|
||||
return g_ActiveConfig.bBBoxEnable && !g_ActiveConfig.BBoxUseFragmentShaderImplementation();
|
||||
}
|
||||
};
|
||||
}; // namespace OGL
|
||||
|
@ -9,19 +9,10 @@ namespace OGL
|
||||
class BoundingBox
|
||||
{
|
||||
public:
|
||||
static void Init(int target_width, int target_height);
|
||||
static void Init();
|
||||
static void Shutdown();
|
||||
|
||||
static void SetTargetSizeChanged(int target_width, int target_height);
|
||||
|
||||
// When SSBO isn't available, the bounding box is calculated directly from the
|
||||
// stencil buffer.
|
||||
static bool NeedsStencilBuffer();
|
||||
// When the stencil buffer is changed, this function needs to be called to
|
||||
// invalidate the cached bounding box data.
|
||||
static void StencilWasUpdated();
|
||||
|
||||
static void Set(int index, int value);
|
||||
static int Get(int index);
|
||||
};
|
||||
};
|
||||
}; // namespace OGL
|
||||
|
@ -1,19 +1,15 @@
|
||||
add_library(videoogl
|
||||
BoundingBox.cpp
|
||||
FramebufferManager.cpp
|
||||
main.cpp
|
||||
NativeVertexFormat.cpp
|
||||
OGLPipeline.cpp
|
||||
OGLShader.cpp
|
||||
OGLTexture.cpp
|
||||
PerfQuery.cpp
|
||||
PostProcessing.cpp
|
||||
ProgramShaderCache.cpp
|
||||
Render.cpp
|
||||
SamplerCache.cpp
|
||||
StreamBuffer.cpp
|
||||
TextureCache.cpp
|
||||
TextureConverter.cpp
|
||||
VertexManager.cpp
|
||||
)
|
||||
|
||||
|
@ -1,634 +0,0 @@
|
||||
// Copyright 2009 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "VideoBackends/OGL/FramebufferManager.h"
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "Common/Common.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "Common/MsgHandler.h"
|
||||
|
||||
#include "Core/HW/Memmap.h"
|
||||
|
||||
#include "VideoBackends/OGL/Render.h"
|
||||
#include "VideoBackends/OGL/SamplerCache.h"
|
||||
#include "VideoBackends/OGL/TextureConverter.h"
|
||||
#include "VideoBackends/OGL/VertexManager.h"
|
||||
|
||||
#include "VideoCommon/OnScreenDisplay.h"
|
||||
#include "VideoCommon/VertexShaderGen.h"
|
||||
#include "VideoCommon/VideoBackendBase.h"
|
||||
|
||||
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() {
|
||||
for (int j = 0; j < %d; ++j) {
|
||||
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";
|
||||
|
||||
constexpr const char* GLSL_EFB_POKE_GEOMETRY_GS = R"GLSL(
|
||||
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";
|
||||
|
||||
namespace OGL
|
||||
{
|
||||
int FramebufferManager::m_targetWidth;
|
||||
int FramebufferManager::m_targetHeight;
|
||||
int FramebufferManager::m_msaaSamples;
|
||||
bool FramebufferManager::m_enable_stencil_buffer;
|
||||
|
||||
GLenum FramebufferManager::m_textureType;
|
||||
std::vector<GLuint> FramebufferManager::m_efbFramebuffer;
|
||||
GLuint FramebufferManager::m_efbColor;
|
||||
GLuint FramebufferManager::m_efbDepth;
|
||||
GLuint FramebufferManager::m_efbColorSwap; // for hot swap when reinterpreting EFB pixel formats
|
||||
|
||||
// Only used in MSAA mode.
|
||||
std::vector<GLuint> FramebufferManager::m_resolvedFramebuffer;
|
||||
GLuint FramebufferManager::m_resolvedColorTexture;
|
||||
GLuint FramebufferManager::m_resolvedDepthTexture;
|
||||
|
||||
// reinterpret pixel format
|
||||
SHADER FramebufferManager::m_pixel_format_shaders[2];
|
||||
|
||||
// EFB pokes
|
||||
GLuint FramebufferManager::m_EfbPokes_VBO;
|
||||
GLuint FramebufferManager::m_EfbPokes_VAO;
|
||||
SHADER FramebufferManager::m_EfbPokes;
|
||||
|
||||
GLuint FramebufferManager::CreateTexture(GLenum texture_type, GLenum internal_format,
|
||||
GLenum pixel_format, GLenum data_type)
|
||||
{
|
||||
GLuint texture;
|
||||
glActiveTexture(GL_TEXTURE9);
|
||||
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);
|
||||
}
|
||||
else if (texture_type == GL_TEXTURE_2D_MULTISAMPLE_ARRAY)
|
||||
{
|
||||
if (g_ogl_config.bSupports3DTextureStorageMultisample)
|
||||
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);
|
||||
}
|
||||
else if (texture_type == GL_TEXTURE_2D_MULTISAMPLE)
|
||||
{
|
||||
if (g_ogl_config.bSupports2DTextureStorageMultisample)
|
||||
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;
|
||||
}
|
||||
|
||||
void FramebufferManager::BindLayeredTexture(GLuint texture, const std::vector<GLuint>& framebuffers,
|
||||
GLenum attachment, GLenum texture_type)
|
||||
{
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, framebuffers[0]);
|
||||
FramebufferTexture(GL_FRAMEBUFFER, attachment, texture_type, texture, 0);
|
||||
// Bind all the other layers as separate FBOs for blitting.
|
||||
for (unsigned int i = 1; i < m_EFBLayers; i++)
|
||||
{
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, framebuffers[i]);
|
||||
glFramebufferTextureLayer(GL_FRAMEBUFFER, attachment, texture, 0, i);
|
||||
}
|
||||
}
|
||||
|
||||
bool FramebufferManager::HasStencilBuffer()
|
||||
{
|
||||
return m_enable_stencil_buffer;
|
||||
}
|
||||
|
||||
FramebufferManager::FramebufferManager(int targetWidth, int targetHeight, int msaaSamples,
|
||||
bool enable_stencil_buffer)
|
||||
{
|
||||
m_efbColor = 0;
|
||||
m_efbDepth = 0;
|
||||
m_efbColorSwap = 0;
|
||||
m_resolvedColorTexture = 0;
|
||||
m_resolvedDepthTexture = 0;
|
||||
|
||||
m_targetWidth = targetWidth;
|
||||
m_targetHeight = targetHeight;
|
||||
m_msaaSamples = msaaSamples;
|
||||
m_enable_stencil_buffer = enable_stencil_buffer;
|
||||
|
||||
// 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);
|
||||
|
||||
m_EFBLayers = (g_ActiveConfig.stereo_mode != StereoMode::Off) ? 2 : 1;
|
||||
m_efbFramebuffer.resize(m_EFBLayers);
|
||||
m_resolvedFramebuffer.resize(m_EFBLayers);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
const bool multilayer = m_EFBLayers > 1;
|
||||
|
||||
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.
|
||||
m_textureType = multilayer ? GL_TEXTURE_2D_MULTISAMPLE_ARRAY : GL_TEXTURE_2D_MULTISAMPLE;
|
||||
|
||||
// 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:
|
||||
GLenum resolvedType = GL_TEXTURE_2D_ARRAY;
|
||||
|
||||
m_resolvedColorTexture = CreateTexture(resolvedType, GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE);
|
||||
m_resolvedDepthTexture =
|
||||
CreateTexture(resolvedType, depth_internal_format, depth_pixel_format, depth_data_type);
|
||||
|
||||
// Bind resolved textures to resolved framebuffer.
|
||||
glGenFramebuffers(m_EFBLayers, m_resolvedFramebuffer.data());
|
||||
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);
|
||||
}
|
||||
|
||||
m_efbColor = CreateTexture(m_textureType, GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE);
|
||||
m_efbDepth =
|
||||
CreateTexture(m_textureType, depth_internal_format, depth_pixel_format, depth_data_type);
|
||||
m_efbColorSwap = CreateTexture(m_textureType, GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE);
|
||||
|
||||
// Bind target textures to EFB framebuffer.
|
||||
glGenFramebuffers(m_EFBLayers, m_efbFramebuffer.data());
|
||||
BindLayeredTexture(m_efbColor, m_efbFramebuffer, GL_COLOR_ATTACHMENT0, m_textureType);
|
||||
BindLayeredTexture(m_efbDepth, m_efbFramebuffer, GL_DEPTH_ATTACHMENT, m_textureType);
|
||||
if (m_enable_stencil_buffer)
|
||||
BindLayeredTexture(m_efbDepth, m_efbFramebuffer, GL_STENCIL_ATTACHMENT, m_textureType);
|
||||
|
||||
// EFB framebuffer is currently bound, make sure to clear it before use.
|
||||
glViewport(0, 0, m_targetWidth, m_targetHeight);
|
||||
glScissor(0, 0, m_targetWidth, m_targetHeight);
|
||||
glClearColor(0.f, 0.f, 0.f, 0.f);
|
||||
glClearDepthf(1.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
if (m_enable_stencil_buffer)
|
||||
{
|
||||
glClearStencil(0);
|
||||
glClear(GL_STENCIL_BUFFER_BIT);
|
||||
}
|
||||
|
||||
// reinterpret pixel format
|
||||
std::string vs = GLSL_REINTERPRET_PIXELFMT_VS;
|
||||
|
||||
// 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;
|
||||
|
||||
if (m_msaaSamples <= 1)
|
||||
{
|
||||
// non-msaa, so just fetch the pixel
|
||||
sampler = StringFromFormat(GLSL_SHADER_FS, multilayer, false);
|
||||
}
|
||||
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.
|
||||
sampler = StringFromFormat(GLSL_SHADER_FS, multilayer, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
// msaa without sample shading: calculate the mean value of the pixel
|
||||
sampler = StringFromFormat(GLSL_SAMPLE_EFB_FS, multilayer, m_msaaSamples, m_msaaSamples);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
ProgramShaderCache::CompileShader(m_pixel_format_shaders[0], vs, ps_rgb8_to_rgba6,
|
||||
multilayer ? gs : "");
|
||||
ProgramShaderCache::CompileShader(m_pixel_format_shaders[1], vs, ps_rgba6_to_rgb8,
|
||||
multilayer ? gs : "");
|
||||
|
||||
const auto prefix = multilayer ? "g" : "v";
|
||||
|
||||
ProgramShaderCache::CompileShader(
|
||||
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) :
|
||||
"");
|
||||
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));
|
||||
glBindBuffer(GL_ARRAY_BUFFER,
|
||||
static_cast<VertexManager*>(g_vertex_manager.get())->GetVertexBufferHandle());
|
||||
|
||||
if (!static_cast<Renderer*>(g_renderer.get())->IsGLES())
|
||||
glEnable(GL_PROGRAM_POINT_SIZE);
|
||||
}
|
||||
|
||||
FramebufferManager::~FramebufferManager()
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
GLuint FramebufferManager::GetEFBColorTexture(const EFBRectangle& sourceRc)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
GLuint FramebufferManager::GetEFBDepthTexture(const EFBRectangle& sourceRc)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
void FramebufferManager::SetFramebuffer(GLuint fb)
|
||||
{
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, fb != 0 ? fb : GetEFBFramebuffer());
|
||||
}
|
||||
|
||||
void FramebufferManager::FramebufferTexture(GLenum target, GLenum attachment, GLenum textarget,
|
||||
GLuint texture, GLint level)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// Apply AA if enabled
|
||||
GLuint FramebufferManager::ResolveAndGetRenderTarget(const EFBRectangle& source_rect)
|
||||
{
|
||||
return GetEFBColorTexture(source_rect);
|
||||
}
|
||||
|
||||
GLuint FramebufferManager::ResolveAndGetDepthTarget(const EFBRectangle& source_rect)
|
||||
{
|
||||
return GetEFBDepthTexture(source_rect);
|
||||
}
|
||||
|
||||
void FramebufferManager::ReinterpretPixelData(unsigned int convtype)
|
||||
{
|
||||
g_renderer->ResetAPIState();
|
||||
|
||||
GLuint src_texture = 0;
|
||||
|
||||
// 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);
|
||||
|
||||
glViewport(0, 0, m_targetWidth, m_targetHeight);
|
||||
glActiveTexture(GL_TEXTURE9);
|
||||
glBindTexture(m_textureType, src_texture);
|
||||
g_sampler_cache->BindNearestSampler(9);
|
||||
|
||||
m_pixel_format_shaders[convtype ? 1 : 0].Bind();
|
||||
ProgramShaderCache::BindVertexFormat(nullptr);
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
glBindTexture(m_textureType, 0);
|
||||
|
||||
g_renderer->RestoreAPIState();
|
||||
}
|
||||
|
||||
void FramebufferManager::PokeEFB(EFBAccessType type, const EfbPokeData* points, size_t num_points)
|
||||
{
|
||||
g_renderer->ResetAPIState();
|
||||
|
||||
if (type == EFBAccessType::PokeZ)
|
||||
{
|
||||
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);
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER,
|
||||
static_cast<VertexManager*>(g_vertex_manager.get())->GetVertexBufferHandle());
|
||||
g_renderer->RestoreAPIState();
|
||||
|
||||
// TODO: Could just update the EFB cache with the new value
|
||||
ClearEFBCache();
|
||||
}
|
||||
|
||||
} // namespace OGL
|
@ -1,127 +0,0 @@
|
||||
// Copyright 2009 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/GL/GLUtil.h"
|
||||
#include "VideoBackends/OGL/ProgramShaderCache.h"
|
||||
#include "VideoBackends/OGL/Render.h"
|
||||
#include "VideoCommon/FramebufferManagerBase.h"
|
||||
|
||||
// On the GameCube, the game sends a request for the graphics processor to
|
||||
// transfer its internal EFB (Embedded Framebuffer) to an area in GameCube RAM
|
||||
// called the XFB (External Framebuffer). The size and location of the XFB is
|
||||
// decided at the time of the copy, and the format is always YUYV. The video
|
||||
// interface is given a pointer to the XFB, which will be decoded and
|
||||
// displayed on the TV.
|
||||
//
|
||||
// There are two ways for Dolphin to emulate this:
|
||||
//
|
||||
// Real XFB mode:
|
||||
//
|
||||
// Dolphin will behave like the GameCube and encode the EFB to
|
||||
// a portion of GameCube RAM. The emulated video interface will decode the data
|
||||
// for output to the screen.
|
||||
//
|
||||
// Advantages: Behaves exactly like the GameCube.
|
||||
// Disadvantages: Resolution will be limited.
|
||||
//
|
||||
// Virtual XFB mode:
|
||||
//
|
||||
// When a request is made to copy the EFB to an XFB, Dolphin
|
||||
// will remember the RAM location and size of the XFB in a Virtual XFB list.
|
||||
// The video interface will look up the XFB in the list and use the enhanced
|
||||
// data stored there, if available.
|
||||
//
|
||||
// Advantages: Enables high resolution graphics, better than real hardware.
|
||||
// Disadvantages: If the GameCube CPU writes directly to the XFB (which is
|
||||
// possible but uncommon), the Virtual XFB will not capture this information.
|
||||
|
||||
// There may be multiple XFBs in GameCube RAM. This is the maximum number to
|
||||
// virtualize.
|
||||
|
||||
namespace OGL
|
||||
{
|
||||
class FramebufferManager : public FramebufferManagerBase
|
||||
{
|
||||
public:
|
||||
FramebufferManager(int targetWidth, int targetHeight, int msaaSamples,
|
||||
bool enable_stencil_buffer);
|
||||
~FramebufferManager();
|
||||
|
||||
// To get the EFB in texture form, these functions may have to transfer
|
||||
// the EFB to a resolved texture first.
|
||||
static GLuint GetEFBColorTexture(const EFBRectangle& sourceRc);
|
||||
static GLuint GetEFBDepthTexture(const EFBRectangle& sourceRc);
|
||||
static void ResolveEFBStencilTexture();
|
||||
|
||||
static GLuint GetEFBFramebuffer(unsigned int layer = 0)
|
||||
{
|
||||
return (layer < m_EFBLayers) ? m_efbFramebuffer[layer] : m_efbFramebuffer.back();
|
||||
}
|
||||
// Resolved framebuffer is only used in MSAA mode.
|
||||
static GLuint GetResolvedFramebuffer();
|
||||
static void SetFramebuffer(GLuint fb);
|
||||
static void FramebufferTexture(GLenum target, GLenum attachment, GLenum textarget, GLuint texture,
|
||||
GLint level);
|
||||
|
||||
// If in MSAA mode, this will perform a resolve of the specified rectangle, and return the resolve
|
||||
// target as a texture ID.
|
||||
// Thus, this call may be expensive. Don't repeat it unnecessarily.
|
||||
// If not in MSAA mode, will just return the render target texture ID.
|
||||
// After calling this, before you render anything else, you MUST bind the framebuffer you want to
|
||||
// draw to.
|
||||
static GLuint ResolveAndGetRenderTarget(const EFBRectangle& rect);
|
||||
|
||||
// Same as above but for the depth Target.
|
||||
// After calling this, before you render anything else, you MUST bind the framebuffer you want to
|
||||
// draw to.
|
||||
static GLuint ResolveAndGetDepthTarget(const EFBRectangle& rect);
|
||||
|
||||
// Convert EFB content on pixel format change.
|
||||
// convtype=0 -> rgb8->rgba6, convtype=2 -> rgba6->rgb8
|
||||
static void ReinterpretPixelData(unsigned int convtype);
|
||||
|
||||
static void PokeEFB(EFBAccessType type, const EfbPokeData* points, size_t num_points);
|
||||
static bool HasStencilBuffer();
|
||||
|
||||
private:
|
||||
GLuint CreateTexture(GLenum texture_type, GLenum internal_format, GLenum pixel_format,
|
||||
GLenum data_type);
|
||||
void BindLayeredTexture(GLuint texture, const std::vector<GLuint>& framebuffers,
|
||||
GLenum attachment, GLenum texture_type);
|
||||
|
||||
static int m_targetWidth;
|
||||
static int m_targetHeight;
|
||||
static int m_msaaSamples;
|
||||
|
||||
static GLenum m_textureType;
|
||||
static std::vector<GLuint> m_efbFramebuffer;
|
||||
static GLuint m_efbColor;
|
||||
static GLuint m_efbDepth;
|
||||
static GLuint
|
||||
m_efbColorSwap; // will be hot swapped with m_efbColor when reinterpreting EFB pixel formats
|
||||
|
||||
static bool m_enable_stencil_buffer;
|
||||
|
||||
// Only used in MSAA mode, TODO: try to avoid them
|
||||
static std::vector<GLuint> m_resolvedFramebuffer;
|
||||
static GLuint m_resolvedColorTexture;
|
||||
static GLuint m_resolvedDepthTexture;
|
||||
|
||||
// For pixel format draw
|
||||
static SHADER m_pixel_format_shaders[2];
|
||||
|
||||
// For EFB pokes
|
||||
static GLuint m_EfbPokes_VBO;
|
||||
static GLuint m_EfbPokes_VAO;
|
||||
static SHADER m_EfbPokes;
|
||||
};
|
||||
|
||||
} // namespace OGL
|
@ -7,6 +7,7 @@
|
||||
#include "Common/MsgHandler.h"
|
||||
|
||||
#include "VideoBackends/OGL/ProgramShaderCache.h"
|
||||
#include "VideoBackends/OGL/Render.h"
|
||||
#include "VideoBackends/OGL/VertexManager.h"
|
||||
|
||||
#include "VideoCommon/NativeVertexFormat.h"
|
||||
@ -18,7 +19,7 @@
|
||||
namespace OGL
|
||||
{
|
||||
std::unique_ptr<NativeVertexFormat>
|
||||
VertexManager::CreateNativeVertexFormat(const PortableVertexDeclaration& vtx_decl)
|
||||
Renderer::CreateNativeVertexFormat(const PortableVertexDeclaration& vtx_decl)
|
||||
{
|
||||
return std::make_unique<GLVertexFormat>(vtx_decl);
|
||||
}
|
||||
@ -44,10 +45,10 @@ static void SetPointer(u32 attrib, u32 stride, const AttributeFormat& format)
|
||||
(u8*)nullptr + format.offset);
|
||||
}
|
||||
|
||||
GLVertexFormat::GLVertexFormat(const PortableVertexDeclaration& _vtx_decl)
|
||||
GLVertexFormat::GLVertexFormat(const PortableVertexDeclaration& vtx_decl)
|
||||
: NativeVertexFormat(vtx_decl)
|
||||
{
|
||||
this->vtx_decl = _vtx_decl;
|
||||
u32 vertex_stride = _vtx_decl.stride;
|
||||
u32 vertex_stride = vtx_decl.stride;
|
||||
|
||||
// We will not allow vertex components causing uneven strides.
|
||||
if (vertex_stride & 3)
|
||||
@ -63,22 +64,22 @@ GLVertexFormat::GLVertexFormat(const PortableVertexDeclaration& _vtx_decl)
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vm->GetIndexBufferHandle());
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vm->GetVertexBufferHandle());
|
||||
|
||||
SetPointer(SHADER_POSITION_ATTRIB, vertex_stride, _vtx_decl.position);
|
||||
SetPointer(SHADER_POSITION_ATTRIB, vertex_stride, vtx_decl.position);
|
||||
|
||||
for (int i = 0; i < 3; i++)
|
||||
SetPointer(SHADER_NORM0_ATTRIB + i, vertex_stride, _vtx_decl.normals[i]);
|
||||
SetPointer(SHADER_NORM0_ATTRIB + i, vertex_stride, vtx_decl.normals[i]);
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
SetPointer(SHADER_COLOR0_ATTRIB + i, vertex_stride, _vtx_decl.colors[i]);
|
||||
SetPointer(SHADER_COLOR0_ATTRIB + i, vertex_stride, vtx_decl.colors[i]);
|
||||
|
||||
for (int i = 0; i < 8; i++)
|
||||
SetPointer(SHADER_TEXTURE0_ATTRIB + i, vertex_stride, _vtx_decl.texcoords[i]);
|
||||
SetPointer(SHADER_TEXTURE0_ATTRIB + i, vertex_stride, vtx_decl.texcoords[i]);
|
||||
|
||||
SetPointer(SHADER_POSMTX_ATTRIB, vertex_stride, _vtx_decl.posmtx);
|
||||
SetPointer(SHADER_POSMTX_ATTRIB, vertex_stride, vtx_decl.posmtx);
|
||||
}
|
||||
|
||||
GLVertexFormat::~GLVertexFormat()
|
||||
{
|
||||
glDeleteVertexArrays(1, &VAO);
|
||||
}
|
||||
}
|
||||
} // namespace OGL
|
||||
|
@ -40,17 +40,13 @@
|
||||
<ClCompile Include="OGLShader.cpp" />
|
||||
<ClCompile Include="OGLTexture.cpp" />
|
||||
<ClCompile Include="BoundingBox.cpp" />
|
||||
<ClCompile Include="FramebufferManager.cpp" />
|
||||
<ClCompile Include="main.cpp" />
|
||||
<ClCompile Include="NativeVertexFormat.cpp" />
|
||||
<ClCompile Include="PerfQuery.cpp" />
|
||||
<ClCompile Include="PostProcessing.cpp" />
|
||||
<ClCompile Include="ProgramShaderCache.cpp" />
|
||||
<ClCompile Include="Render.cpp" />
|
||||
<ClCompile Include="SamplerCache.cpp" />
|
||||
<ClCompile Include="StreamBuffer.cpp" />
|
||||
<ClCompile Include="TextureCache.cpp" />
|
||||
<ClCompile Include="TextureConverter.cpp" />
|
||||
<ClCompile Include="VertexManager.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
@ -58,16 +54,12 @@
|
||||
<ClInclude Include="OGLShader.h" />
|
||||
<ClInclude Include="OGLTexture.h" />
|
||||
<ClInclude Include="BoundingBox.h" />
|
||||
<ClInclude Include="FramebufferManager.h" />
|
||||
<ClInclude Include="GPUTimer.h" />
|
||||
<ClInclude Include="PerfQuery.h" />
|
||||
<ClInclude Include="PostProcessing.h" />
|
||||
<ClInclude Include="ProgramShaderCache.h" />
|
||||
<ClInclude Include="Render.h" />
|
||||
<ClInclude Include="SamplerCache.h" />
|
||||
<ClInclude Include="StreamBuffer.h" />
|
||||
<ClInclude Include="TextureCache.h" />
|
||||
<ClInclude Include="TextureConverter.h" />
|
||||
<ClInclude Include="VertexManager.h" />
|
||||
<ClInclude Include="VideoBackend.h" />
|
||||
</ItemGroup>
|
||||
|
@ -18,21 +18,12 @@
|
||||
<ClCompile Include="VertexManager.cpp">
|
||||
<Filter>Decoder</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="TextureConverter.cpp">
|
||||
<Filter>GLUtil</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="BoundingBox.cpp">
|
||||
<Filter>Render</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="FramebufferManager.cpp">
|
||||
<Filter>Render</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="PerfQuery.cpp">
|
||||
<Filter>Render</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="PostProcessing.cpp">
|
||||
<Filter>Render</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ProgramShaderCache.cpp">
|
||||
<Filter>Render</Filter>
|
||||
</ClCompile>
|
||||
@ -42,9 +33,6 @@
|
||||
<ClCompile Include="StreamBuffer.cpp">
|
||||
<Filter>Render</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="TextureCache.cpp">
|
||||
<Filter>Render</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="main.cpp" />
|
||||
<ClCompile Include="SamplerCache.cpp" />
|
||||
<ClCompile Include="OGLTexture.cpp">
|
||||
@ -61,21 +49,12 @@
|
||||
<ClInclude Include="VertexManager.h">
|
||||
<Filter>Decoder</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="TextureConverter.h">
|
||||
<Filter>GLUtil</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="BoundingBox.h">
|
||||
<Filter>Render</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="FramebufferManager.h">
|
||||
<Filter>Render</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="PerfQuery.h">
|
||||
<Filter>Render</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="PostProcessing.h">
|
||||
<Filter>Render</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ProgramShaderCache.h">
|
||||
<Filter>Render</Filter>
|
||||
</ClInclude>
|
||||
@ -85,9 +64,6 @@
|
||||
<ClInclude Include="StreamBuffer.h">
|
||||
<Filter>Render</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="TextureCache.h">
|
||||
<Filter>Render</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="SamplerCache.h" />
|
||||
<ClInclude Include="VideoBackend.h" />
|
||||
<ClInclude Include="GPUTimer.h">
|
||||
|
@ -24,23 +24,24 @@ static GLenum GetGLShaderTypeForStage(ShaderStage stage)
|
||||
}
|
||||
}
|
||||
|
||||
OGLShader::OGLShader(ShaderStage stage, GLenum gl_type, GLuint shader_id)
|
||||
: AbstractShader(stage), m_type(gl_type), m_id(shader_id)
|
||||
OGLShader::OGLShader(ShaderStage stage, GLenum gl_type, GLuint gl_id)
|
||||
: AbstractShader(stage), m_id(ProgramShaderCache::GenerateShaderID()), m_type(gl_type),
|
||||
m_gl_id(gl_id)
|
||||
{
|
||||
}
|
||||
|
||||
OGLShader::OGLShader(GLuint compute_program_id)
|
||||
: AbstractShader(ShaderStage::Compute), m_type(GL_COMPUTE_SHADER),
|
||||
m_compute_program_id(compute_program_id)
|
||||
OGLShader::OGLShader(GLuint gl_compute_program_id)
|
||||
: AbstractShader(ShaderStage::Compute), m_id(ProgramShaderCache::GenerateShaderID()),
|
||||
m_type(GL_COMPUTE_SHADER), m_gl_compute_program_id(gl_compute_program_id)
|
||||
{
|
||||
}
|
||||
|
||||
OGLShader::~OGLShader()
|
||||
{
|
||||
if (m_stage != ShaderStage::Compute)
|
||||
glDeleteShader(m_id);
|
||||
glDeleteShader(m_gl_id);
|
||||
else
|
||||
glDeleteProgram(m_compute_program_id);
|
||||
glDeleteProgram(m_gl_compute_program_id);
|
||||
}
|
||||
|
||||
bool OGLShader::HasBinary() const
|
||||
|
@ -16,13 +16,14 @@ namespace OGL
|
||||
class OGLShader final : public AbstractShader
|
||||
{
|
||||
public:
|
||||
explicit OGLShader(ShaderStage stage, GLenum gl_type, GLuint shader_id);
|
||||
explicit OGLShader(GLuint compute_program_id);
|
||||
explicit OGLShader(ShaderStage stage, GLenum gl_type, GLuint gl_id);
|
||||
explicit OGLShader(GLuint gl_compute_program_id);
|
||||
~OGLShader() override;
|
||||
|
||||
u64 GetID() const { return m_id; }
|
||||
GLenum GetGLShaderType() const { return m_type; }
|
||||
GLuint GetGLShaderID() const { return m_id; }
|
||||
GLuint GetGLComputeProgramID() const { return m_compute_program_id; }
|
||||
GLuint GetGLShaderID() const { return m_gl_id; }
|
||||
GLuint GetGLComputeProgramID() const { return m_gl_compute_program_id; }
|
||||
bool HasBinary() const override;
|
||||
BinaryData GetBinary() const override;
|
||||
|
||||
@ -30,9 +31,10 @@ public:
|
||||
size_t length);
|
||||
|
||||
private:
|
||||
u64 m_id;
|
||||
GLenum m_type;
|
||||
GLuint m_id = 0;
|
||||
GLuint m_compute_program_id = 0;
|
||||
GLuint m_gl_id = 0;
|
||||
GLuint m_gl_compute_program_id = 0;
|
||||
};
|
||||
|
||||
} // namespace OGL
|
||||
|
@ -6,13 +6,8 @@
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/MsgHandler.h"
|
||||
|
||||
#include "VideoBackends/OGL/FramebufferManager.h"
|
||||
#include "VideoBackends/OGL/OGLTexture.h"
|
||||
#include "VideoBackends/OGL/SamplerCache.h"
|
||||
#include "VideoBackends/OGL/TextureCache.h"
|
||||
|
||||
#include "VideoCommon/ImageWrite.h"
|
||||
#include "VideoCommon/TextureConfig.h"
|
||||
|
||||
namespace OGL
|
||||
{
|
||||
@ -115,10 +110,9 @@ OGLTexture::OGLTexture(const TextureConfig& tex_config) : AbstractTexture(tex_co
|
||||
DEBUG_ASSERT_MSG(VIDEO, !tex_config.IsMultisampled() || tex_config.levels == 1,
|
||||
"OpenGL does not support multisampled textures with mip levels");
|
||||
|
||||
GLenum target =
|
||||
tex_config.IsMultisampled() ? GL_TEXTURE_2D_MULTISAMPLE_ARRAY : GL_TEXTURE_2D_ARRAY;
|
||||
const GLenum target = GetGLTarget();
|
||||
glGenTextures(1, &m_texId);
|
||||
glActiveTexture(GL_TEXTURE9);
|
||||
glActiveTexture(GL_MUTABLE_TEXTURE_INDEX);
|
||||
glBindTexture(target, m_texId);
|
||||
|
||||
glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, m_config.levels - 1);
|
||||
@ -139,7 +133,7 @@ OGLTexture::OGLTexture(const TextureConfig& tex_config) : AbstractTexture(tex_co
|
||||
m_config.layers);
|
||||
}
|
||||
|
||||
if (m_config.rendertarget)
|
||||
if (m_config.IsRenderTarget())
|
||||
{
|
||||
// We can't render to compressed formats.
|
||||
ASSERT(!IsCompressedFormat(m_config.format));
|
||||
@ -147,40 +141,19 @@ OGLTexture::OGLTexture(const TextureConfig& tex_config) : AbstractTexture(tex_co
|
||||
{
|
||||
for (u32 level = 0; level < m_config.levels; level++)
|
||||
{
|
||||
glTexImage3D(target, level, GL_RGBA, std::max(m_config.width >> level, 1u),
|
||||
std::max(m_config.height >> level, 1u), m_config.layers, 0, GL_RGBA,
|
||||
GL_UNSIGNED_BYTE, nullptr);
|
||||
glTexImage3D(target, level, gl_internal_format, std::max(m_config.width >> level, 1u),
|
||||
std::max(m_config.height >> level, 1u), m_config.layers, 0,
|
||||
GetGLFormatForTextureFormat(m_config.format),
|
||||
GetGLTypeForTextureFormat(m_config.format), nullptr);
|
||||
}
|
||||
}
|
||||
glGenFramebuffers(1, &m_framebuffer);
|
||||
FramebufferManager::SetFramebuffer(m_framebuffer);
|
||||
FramebufferManager::FramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target, m_texId,
|
||||
0);
|
||||
|
||||
// We broke the framebuffer binding here, and need to restore it, as the CreateTexture
|
||||
// method is in the base renderer class and can be called by VideoCommon.
|
||||
FramebufferManager::SetFramebuffer(0);
|
||||
}
|
||||
}
|
||||
|
||||
OGLTexture::~OGLTexture()
|
||||
{
|
||||
g_renderer->UnbindTexture(this);
|
||||
if (m_texId)
|
||||
glDeleteTextures(1, &m_texId);
|
||||
|
||||
if (m_framebuffer)
|
||||
glDeleteFramebuffers(1, &m_framebuffer);
|
||||
}
|
||||
|
||||
GLuint OGLTexture::GetRawTexIdentifier() const
|
||||
{
|
||||
return m_texId;
|
||||
}
|
||||
|
||||
GLuint OGLTexture::GetFramebuffer() const
|
||||
{
|
||||
return m_framebuffer;
|
||||
Renderer::GetInstance()->UnbindTexture(this);
|
||||
glDeleteTextures(1, &m_texId);
|
||||
}
|
||||
|
||||
void OGLTexture::CopyRectangleFromTexture(const AbstractTexture* src,
|
||||
@ -188,19 +161,18 @@ void OGLTexture::CopyRectangleFromTexture(const AbstractTexture* src,
|
||||
u32 src_level, const MathUtil::Rectangle<int>& dst_rect,
|
||||
u32 dst_layer, u32 dst_level)
|
||||
{
|
||||
const OGLTexture* srcentry = static_cast<const OGLTexture*>(src);
|
||||
const OGLTexture* src_gltex = static_cast<const OGLTexture*>(src);
|
||||
ASSERT(src_rect.GetWidth() == dst_rect.GetWidth() &&
|
||||
src_rect.GetHeight() == dst_rect.GetHeight());
|
||||
if (g_ogl_config.bSupportsCopySubImage)
|
||||
{
|
||||
glCopyImageSubData(srcentry->m_texId, GL_TEXTURE_2D_ARRAY, src_level, src_rect.left,
|
||||
src_rect.top, src_layer, m_texId, GL_TEXTURE_2D_ARRAY, dst_level,
|
||||
dst_rect.left, dst_rect.top, dst_layer, dst_rect.GetWidth(),
|
||||
dst_rect.GetHeight(), 1);
|
||||
glCopyImageSubData(src_gltex->m_texId, src_gltex->GetGLTarget(), src_level, src_rect.left,
|
||||
src_rect.top, src_layer, m_texId, GetGLTarget(), dst_level, dst_rect.left,
|
||||
dst_rect.top, dst_layer, dst_rect.GetWidth(), dst_rect.GetHeight(), 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
BlitFramebuffer(const_cast<OGLTexture*>(srcentry), src_rect, src_layer, src_level, dst_rect,
|
||||
BlitFramebuffer(const_cast<OGLTexture*>(src_gltex), src_rect, src_layer, src_level, dst_rect,
|
||||
dst_layer, dst_level);
|
||||
}
|
||||
}
|
||||
@ -210,28 +182,12 @@ void OGLTexture::BlitFramebuffer(OGLTexture* srcentry, const MathUtil::Rectangle
|
||||
const MathUtil::Rectangle<int>& dst_rect, u32 dst_layer,
|
||||
u32 dst_level)
|
||||
{
|
||||
// If it isn't a single leveled/layered texture, we need to update the framebuffer.
|
||||
bool update_src_framebuffer =
|
||||
srcentry->m_framebuffer == 0 || srcentry->m_config.layers != 0 || src_level != 0;
|
||||
bool update_dst_framebuffer = m_framebuffer == 0 || m_config.layers != 0 || dst_level != 0;
|
||||
if (!m_framebuffer)
|
||||
glGenFramebuffers(1, &m_framebuffer);
|
||||
if (!srcentry->m_framebuffer)
|
||||
glGenFramebuffers(1, &const_cast<OGLTexture*>(srcentry)->m_framebuffer);
|
||||
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, srcentry->m_framebuffer);
|
||||
if (update_src_framebuffer)
|
||||
{
|
||||
glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, srcentry->m_texId,
|
||||
src_level, src_layer);
|
||||
}
|
||||
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_framebuffer);
|
||||
if (update_dst_framebuffer)
|
||||
{
|
||||
glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_texId, dst_level,
|
||||
dst_layer);
|
||||
}
|
||||
Renderer::GetInstance()->BindSharedReadFramebuffer();
|
||||
glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, srcentry->m_texId, src_level,
|
||||
src_layer);
|
||||
Renderer::GetInstance()->BindSharedDrawFramebuffer();
|
||||
glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_texId, dst_level,
|
||||
dst_layer);
|
||||
|
||||
// glBlitFramebuffer is still affected by the scissor test, which is enabled by default.
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
@ -239,50 +195,10 @@ void OGLTexture::BlitFramebuffer(OGLTexture* srcentry, const MathUtil::Rectangle
|
||||
glBlitFramebuffer(src_rect.left, src_rect.top, src_rect.right, src_rect.bottom, dst_rect.left,
|
||||
dst_rect.top, dst_rect.right, dst_rect.bottom, GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
||||
|
||||
if (update_src_framebuffer)
|
||||
{
|
||||
FramebufferManager::FramebufferTexture(
|
||||
GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
|
||||
srcentry->m_config.IsMultisampled() ? GL_TEXTURE_2D_MULTISAMPLE_ARRAY : GL_TEXTURE_2D_ARRAY,
|
||||
srcentry->m_texId, 0);
|
||||
}
|
||||
if (update_dst_framebuffer)
|
||||
{
|
||||
FramebufferManager::FramebufferTexture(
|
||||
GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
|
||||
m_config.IsMultisampled() ? GL_TEXTURE_2D_MULTISAMPLE_ARRAY : GL_TEXTURE_2D_ARRAY, m_texId,
|
||||
0);
|
||||
}
|
||||
|
||||
// The default state for the scissor test is enabled. We don't need to do a full state
|
||||
// restore, as the framebuffer and scissor test are the only things we changed.
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
FramebufferManager::SetFramebuffer(0);
|
||||
}
|
||||
|
||||
void OGLTexture::ScaleRectangleFromTexture(const AbstractTexture* source,
|
||||
const MathUtil::Rectangle<int>& srcrect,
|
||||
const MathUtil::Rectangle<int>& dstrect)
|
||||
{
|
||||
const OGLTexture* srcentry = static_cast<const OGLTexture*>(source);
|
||||
if (!m_framebuffer)
|
||||
{
|
||||
glGenFramebuffers(1, &m_framebuffer);
|
||||
FramebufferManager::SetFramebuffer(m_framebuffer);
|
||||
FramebufferManager::FramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
|
||||
GL_TEXTURE_2D_ARRAY, m_texId, 0);
|
||||
}
|
||||
g_renderer->ResetAPIState();
|
||||
FramebufferManager::SetFramebuffer(m_framebuffer);
|
||||
glActiveTexture(GL_TEXTURE9);
|
||||
glBindTexture(GL_TEXTURE_2D_ARRAY, srcentry->m_texId);
|
||||
g_sampler_cache->BindLinearSampler(9);
|
||||
glViewport(dstrect.left, dstrect.top, dstrect.GetWidth(), dstrect.GetHeight());
|
||||
TextureCache::GetInstance()->GetColorCopyProgram().Bind();
|
||||
glUniform4f(TextureCache::GetInstance()->GetColorCopyPositionUniform(), float(srcrect.left),
|
||||
float(srcrect.top), float(srcrect.GetWidth()), float(srcrect.GetHeight()));
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
g_renderer->RestoreAPIState();
|
||||
Renderer::GetInstance()->RestoreFramebufferBinding();
|
||||
}
|
||||
|
||||
void OGLTexture::ResolveFromTexture(const AbstractTexture* src,
|
||||
@ -307,8 +223,9 @@ void OGLTexture::Load(u32 level, u32 width, u32 height, u32 row_length, const u8
|
||||
std::max(1u, m_config.width >> level), std::max(1u, m_config.height >> level), width,
|
||||
height);
|
||||
|
||||
glActiveTexture(GL_TEXTURE9);
|
||||
glBindTexture(GL_TEXTURE_2D_ARRAY, m_texId);
|
||||
const GLenum target = GetGLTarget();
|
||||
glActiveTexture(GL_MUTABLE_TEXTURE_INDEX);
|
||||
glBindTexture(target, m_texId);
|
||||
|
||||
if (row_length != width)
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, row_length);
|
||||
@ -318,12 +235,12 @@ void OGLTexture::Load(u32 level, u32 width, u32 height, u32 row_length, const u8
|
||||
{
|
||||
if (g_ogl_config.bSupportsTextureStorage)
|
||||
{
|
||||
glCompressedTexSubImage3D(GL_TEXTURE_2D_ARRAY, level, 0, 0, 0, width, height, 1,
|
||||
gl_internal_format, static_cast<GLsizei>(buffer_size), buffer);
|
||||
glCompressedTexSubImage3D(target, level, 0, 0, 0, width, height, 1, gl_internal_format,
|
||||
static_cast<GLsizei>(buffer_size), buffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
glCompressedTexImage3D(GL_TEXTURE_2D_ARRAY, level, gl_internal_format, width, height, 1, 0,
|
||||
glCompressedTexImage3D(target, level, gl_internal_format, width, height, 1, 0,
|
||||
static_cast<GLsizei>(buffer_size), buffer);
|
||||
}
|
||||
}
|
||||
@ -333,13 +250,12 @@ void OGLTexture::Load(u32 level, u32 width, u32 height, u32 row_length, const u8
|
||||
GLenum gl_type = GetGLTypeForTextureFormat(m_config.format);
|
||||
if (g_ogl_config.bSupportsTextureStorage)
|
||||
{
|
||||
glTexSubImage3D(GL_TEXTURE_2D_ARRAY, level, 0, 0, 0, width, height, 1, gl_format, gl_type,
|
||||
buffer);
|
||||
glTexSubImage3D(target, level, 0, 0, 0, width, height, 1, gl_format, gl_type, buffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
glTexImage3D(GL_TEXTURE_2D_ARRAY, level, gl_internal_format, width, height, 1, 0, gl_format,
|
||||
gl_type, buffer);
|
||||
glTexImage3D(target, level, gl_internal_format, width, height, 1, 0, gl_format, gl_type,
|
||||
buffer);
|
||||
}
|
||||
}
|
||||
|
||||
@ -347,6 +263,11 @@ void OGLTexture::Load(u32 level, u32 width, u32 height, u32 row_length, const u8
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
||||
}
|
||||
|
||||
GLenum OGLTexture::GetGLFormatForImageTexture() const
|
||||
{
|
||||
return GetGLInternalFormatForTextureFormat(m_config.format, true);
|
||||
}
|
||||
|
||||
OGLStagingTexture::OGLStagingTexture(StagingTextureType type, const TextureConfig& config,
|
||||
GLenum target, GLuint buffer_name, size_t buffer_size,
|
||||
char* map_ptr, size_t map_stride)
|
||||
@ -405,8 +326,7 @@ std::unique_ptr<OGLStagingTexture> OGLStagingTexture::Create(StagingTextureType
|
||||
}
|
||||
|
||||
glBufferStorage(target, buffer_size, nullptr, buffer_flags);
|
||||
buffer_ptr =
|
||||
reinterpret_cast<char*>(glMapBufferRange(GL_PIXEL_PACK_BUFFER, 0, buffer_size, map_flags));
|
||||
buffer_ptr = reinterpret_cast<char*>(glMapBufferRange(target, 0, buffer_size, map_flags));
|
||||
ASSERT(buffer_ptr != nullptr);
|
||||
}
|
||||
else
|
||||
@ -426,7 +346,7 @@ void OGLStagingTexture::CopyFromTexture(const AbstractTexture* src,
|
||||
const MathUtil::Rectangle<int>& src_rect, u32 src_layer,
|
||||
u32 src_level, const MathUtil::Rectangle<int>& dst_rect)
|
||||
{
|
||||
ASSERT(m_type == StagingTextureType::Readback);
|
||||
ASSERT(m_type == StagingTextureType::Readback || m_type == StagingTextureType::Mutable);
|
||||
ASSERT(src_rect.GetWidth() == dst_rect.GetWidth() &&
|
||||
src_rect.GetHeight() == dst_rect.GetHeight());
|
||||
ASSERT(src_rect.left >= 0 && static_cast<u32>(src_rect.right) <= src->GetConfig().width &&
|
||||
@ -443,40 +363,37 @@ void OGLStagingTexture::CopyFromTexture(const AbstractTexture* src,
|
||||
glPixelStorei(GL_PACK_ROW_LENGTH, m_config.width);
|
||||
|
||||
const OGLTexture* gltex = static_cast<const OGLTexture*>(src);
|
||||
size_t dst_offset = dst_rect.top * m_config.GetStride() + dst_rect.left * m_texel_size;
|
||||
const size_t dst_offset = dst_rect.top * m_config.GetStride() + dst_rect.left * m_texel_size;
|
||||
|
||||
// If we don't have a FBO associated with this texture, we need to use a slow path.
|
||||
if (gltex->GetFramebuffer() != 0 && src_layer == 0 && src_level == 0)
|
||||
// Prefer glGetTextureSubImage(), when available.
|
||||
if (g_ogl_config.bSupportsTextureSubImage)
|
||||
{
|
||||
// This texture has a framebuffer, so we can use glReadPixels().
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, gltex->GetFramebuffer());
|
||||
glReadPixels(src_rect.left, src_rect.top, src_rect.GetWidth(), src_rect.GetHeight(),
|
||||
GetGLFormatForTextureFormat(m_config.format),
|
||||
GetGLTypeForTextureFormat(m_config.format), reinterpret_cast<void*>(dst_offset));
|
||||
|
||||
// Reset both read/draw framebuffers.
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, FramebufferManager::GetEFBFramebuffer());
|
||||
glGetTextureSubImage(
|
||||
gltex->GetGLTextureId(), src_level, src_rect.left, src_rect.top, src_layer,
|
||||
src_rect.GetWidth(), src_rect.GetHeight(), 1, GetGLFormatForTextureFormat(src->GetFormat()),
|
||||
GetGLTypeForTextureFormat(src->GetFormat()),
|
||||
static_cast<GLsizei>(m_buffer_size - dst_offset), reinterpret_cast<void*>(dst_offset));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (g_ogl_config.bSupportsTextureSubImage)
|
||||
// Mutate the shared framebuffer.
|
||||
Renderer::GetInstance()->BindSharedReadFramebuffer();
|
||||
if (AbstractTexture::IsDepthFormat(gltex->GetFormat()))
|
||||
{
|
||||
glGetTextureSubImage(
|
||||
gltex->GetRawTexIdentifier(), src_level, src_rect.left, src_rect.top, src_layer,
|
||||
src_rect.GetWidth(), src_rect.GetHeight(), 1,
|
||||
GetGLFormatForTextureFormat(m_config.format), GetGLTypeForTextureFormat(m_config.format),
|
||||
static_cast<GLsizei>(m_buffer_size - dst_offset), reinterpret_cast<void*>(dst_offset));
|
||||
glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, 0, 0, 0);
|
||||
glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, gltex->GetGLTextureId(),
|
||||
src_level, src_layer);
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: Investigate whether it's faster to use glReadPixels() with a framebuffer, since we're
|
||||
// copying the whole texture, which may waste bandwidth. So we're trading CPU work in creating
|
||||
// the framebuffer for GPU work in copying potentially redundant texels.
|
||||
glActiveTexture(GL_TEXTURE9);
|
||||
glBindTexture(GL_TEXTURE_2D_ARRAY, gltex->GetRawTexIdentifier());
|
||||
glGetTexImage(GL_TEXTURE_2D_ARRAY, src_level, GetGLFormatForTextureFormat(m_config.format),
|
||||
GetGLTypeForTextureFormat(m_config.format), nullptr);
|
||||
glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, gltex->GetGLTextureId(),
|
||||
src_level, src_layer);
|
||||
glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, 0, 0, 0);
|
||||
}
|
||||
glReadPixels(src_rect.left, src_rect.top, src_rect.GetWidth(), src_rect.GetHeight(),
|
||||
GetGLFormatForTextureFormat(src->GetFormat()),
|
||||
GetGLTypeForTextureFormat(src->GetFormat()), reinterpret_cast<void*>(dst_offset));
|
||||
Renderer::GetInstance()->RestoreFramebufferBinding();
|
||||
}
|
||||
|
||||
glPixelStorei(GL_PACK_ROW_LENGTH, 0);
|
||||
@ -501,7 +418,7 @@ void OGLStagingTexture::CopyToTexture(const MathUtil::Rectangle<int>& src_rect,
|
||||
const MathUtil::Rectangle<int>& dst_rect, u32 dst_layer,
|
||||
u32 dst_level)
|
||||
{
|
||||
ASSERT(m_type == StagingTextureType::Upload);
|
||||
ASSERT(m_type == StagingTextureType::Upload || m_type == StagingTextureType::Mutable);
|
||||
ASSERT(src_rect.GetWidth() == dst_rect.GetWidth() &&
|
||||
src_rect.GetHeight() == dst_rect.GetHeight());
|
||||
ASSERT(src_rect.left >= 0 && static_cast<u32>(src_rect.right) <= m_config.width &&
|
||||
@ -509,8 +426,9 @@ void OGLStagingTexture::CopyToTexture(const MathUtil::Rectangle<int>& src_rect,
|
||||
ASSERT(dst_rect.left >= 0 && static_cast<u32>(dst_rect.right) <= dst->GetConfig().width &&
|
||||
dst_rect.top >= 0 && static_cast<u32>(dst_rect.bottom) <= dst->GetConfig().height);
|
||||
|
||||
size_t src_offset = src_rect.top * m_config.GetStride() + src_rect.left * m_texel_size;
|
||||
size_t copy_size = src_rect.GetHeight() * m_config.GetStride();
|
||||
const OGLTexture* gltex = static_cast<const OGLTexture*>(dst);
|
||||
const size_t src_offset = src_rect.top * m_config.GetStride() + src_rect.left * m_texel_size;
|
||||
const size_t copy_size = src_rect.GetHeight() * m_config.GetStride();
|
||||
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, m_buffer_name);
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, m_config.width);
|
||||
@ -533,12 +451,12 @@ void OGLStagingTexture::CopyToTexture(const MathUtil::Rectangle<int>& src_rect,
|
||||
}
|
||||
|
||||
// Copy from the staging buffer to the texture object.
|
||||
glActiveTexture(GL_TEXTURE9);
|
||||
glBindTexture(GL_TEXTURE_2D_ARRAY, static_cast<const OGLTexture*>(dst)->GetRawTexIdentifier());
|
||||
glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, dst_rect.left, dst_rect.top, dst_layer,
|
||||
dst_rect.GetWidth(), dst_rect.GetHeight(), 1,
|
||||
GetGLFormatForTextureFormat(m_config.format),
|
||||
GetGLTypeForTextureFormat(m_config.format), reinterpret_cast<void*>(src_offset));
|
||||
const GLenum target = gltex->GetGLTarget();
|
||||
glActiveTexture(GL_MUTABLE_TEXTURE_INDEX);
|
||||
glBindTexture(target, gltex->GetGLTextureId());
|
||||
glTexSubImage3D(target, 0, dst_rect.left, dst_rect.top, dst_layer, dst_rect.GetWidth(),
|
||||
dst_rect.GetHeight(), 1, GetGLFormatForTextureFormat(dst->GetFormat()),
|
||||
GetGLTypeForTextureFormat(dst->GetFormat()), reinterpret_cast<void*>(src_offset));
|
||||
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
|
||||
@ -602,10 +520,13 @@ void OGLStagingTexture::Unmap()
|
||||
m_map_pointer = nullptr;
|
||||
}
|
||||
|
||||
OGLFramebuffer::OGLFramebuffer(AbstractTextureFormat color_format,
|
||||
OGLFramebuffer::OGLFramebuffer(AbstractTexture* color_attachment, AbstractTexture* depth_attachment,
|
||||
AbstractTextureFormat color_format,
|
||||
AbstractTextureFormat depth_format, u32 width, u32 height,
|
||||
u32 layers, u32 samples, GLuint fbo)
|
||||
: AbstractFramebuffer(color_format, depth_format, width, height, layers, samples), m_fbo(fbo)
|
||||
: AbstractFramebuffer(color_attachment, depth_attachment, color_format, depth_format, width,
|
||||
height, layers, samples),
|
||||
m_fbo(fbo)
|
||||
{
|
||||
}
|
||||
|
||||
@ -614,8 +535,8 @@ OGLFramebuffer::~OGLFramebuffer()
|
||||
glDeleteFramebuffers(1, &m_fbo);
|
||||
}
|
||||
|
||||
std::unique_ptr<OGLFramebuffer> OGLFramebuffer::Create(const OGLTexture* color_attachment,
|
||||
const OGLTexture* depth_attachment)
|
||||
std::unique_ptr<OGLFramebuffer> OGLFramebuffer::Create(OGLTexture* color_attachment,
|
||||
OGLTexture* depth_attachment)
|
||||
{
|
||||
if (!ValidateConfig(color_attachment, depth_attachment))
|
||||
return nullptr;
|
||||
@ -638,13 +559,13 @@ std::unique_ptr<OGLFramebuffer> OGLFramebuffer::Create(const OGLTexture* color_a
|
||||
{
|
||||
if (color_attachment->GetConfig().layers > 1)
|
||||
{
|
||||
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
|
||||
color_attachment->GetRawTexIdentifier(), 0);
|
||||
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, color_attachment->GetGLTextureId(),
|
||||
0);
|
||||
}
|
||||
else
|
||||
{
|
||||
glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
|
||||
color_attachment->GetRawTexIdentifier(), 0, 0);
|
||||
color_attachment->GetGLTextureId(), 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@ -655,19 +576,26 @@ std::unique_ptr<OGLFramebuffer> OGLFramebuffer::Create(const OGLTexture* color_a
|
||||
GL_DEPTH_ATTACHMENT;
|
||||
if (depth_attachment->GetConfig().layers > 1)
|
||||
{
|
||||
glFramebufferTexture(GL_FRAMEBUFFER, attachment, depth_attachment->GetRawTexIdentifier(), 0);
|
||||
glFramebufferTexture(GL_FRAMEBUFFER, attachment, depth_attachment->GetGLTextureId(), 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
glFramebufferTextureLayer(GL_FRAMEBUFFER, attachment, depth_attachment->GetRawTexIdentifier(),
|
||||
0, 0);
|
||||
glFramebufferTextureLayer(GL_FRAMEBUFFER, attachment, depth_attachment->GetGLTextureId(), 0,
|
||||
0);
|
||||
}
|
||||
}
|
||||
|
||||
DEBUG_ASSERT(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
|
||||
FramebufferManager::SetFramebuffer(0);
|
||||
return std::make_unique<OGLFramebuffer>(color_format, depth_format, width, height, layers,
|
||||
samples, fbo);
|
||||
Renderer::GetInstance()->RestoreFramebufferBinding();
|
||||
|
||||
return std::make_unique<OGLFramebuffer>(color_attachment, depth_attachment, color_format,
|
||||
depth_format, width, height, layers, samples, fbo);
|
||||
}
|
||||
|
||||
void OGLFramebuffer::UpdateDimensions(u32 width, u32 height)
|
||||
{
|
||||
m_width = width;
|
||||
m_height = height;
|
||||
}
|
||||
|
||||
} // namespace OGL
|
||||
|
@ -25,16 +25,17 @@ public:
|
||||
const MathUtil::Rectangle<int>& src_rect, u32 src_layer,
|
||||
u32 src_level, const MathUtil::Rectangle<int>& dst_rect,
|
||||
u32 dst_layer, u32 dst_level) override;
|
||||
void ScaleRectangleFromTexture(const AbstractTexture* source,
|
||||
const MathUtil::Rectangle<int>& srcrect,
|
||||
const MathUtil::Rectangle<int>& dstrect) override;
|
||||
void ResolveFromTexture(const AbstractTexture* src, const MathUtil::Rectangle<int>& rect,
|
||||
u32 layer, u32 level) override;
|
||||
void Load(u32 level, u32 width, u32 height, u32 row_length, const u8* buffer,
|
||||
size_t buffer_size) override;
|
||||
|
||||
GLuint GetRawTexIdentifier() const;
|
||||
GLuint GetFramebuffer() const;
|
||||
GLuint GetGLTextureId() const { return m_texId; }
|
||||
GLenum GetGLTarget() const
|
||||
{
|
||||
return IsMultisampled() ? GL_TEXTURE_2D_MULTISAMPLE_ARRAY : GL_TEXTURE_2D_ARRAY;
|
||||
}
|
||||
GLenum GetGLFormatForImageTexture() const;
|
||||
|
||||
private:
|
||||
void BlitFramebuffer(OGLTexture* srcentry, const MathUtil::Rectangle<int>& src_rect,
|
||||
@ -42,7 +43,6 @@ private:
|
||||
u32 dst_layer, u32 dst_level);
|
||||
|
||||
GLuint m_texId;
|
||||
GLuint m_framebuffer = 0;
|
||||
};
|
||||
|
||||
class OGLStagingTexture final : public AbstractStagingTexture
|
||||
@ -79,13 +79,18 @@ private:
|
||||
class OGLFramebuffer final : public AbstractFramebuffer
|
||||
{
|
||||
public:
|
||||
OGLFramebuffer(AbstractTextureFormat color_format, AbstractTextureFormat depth_format, u32 width,
|
||||
OGLFramebuffer(AbstractTexture* color_attachment, AbstractTexture* depth_attachment,
|
||||
AbstractTextureFormat color_format, AbstractTextureFormat depth_format, u32 width,
|
||||
u32 height, u32 layers, u32 samples, GLuint fbo);
|
||||
~OGLFramebuffer() override;
|
||||
|
||||
static std::unique_ptr<OGLFramebuffer> Create(OGLTexture* color_attachment,
|
||||
OGLTexture* depth_attachment);
|
||||
|
||||
GLuint GetFBO() const { return m_fbo; }
|
||||
static std::unique_ptr<OGLFramebuffer> Create(const OGLTexture* color_attachment,
|
||||
const OGLTexture* depth_attachment);
|
||||
|
||||
// Used for updating the dimensions of the system/window framebuffer.
|
||||
void UpdateDimensions(u32 width, u32 height);
|
||||
|
||||
protected:
|
||||
GLuint m_fbo;
|
||||
|
@ -1,273 +0,0 @@
|
||||
// Copyright 2009 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "VideoBackends/OGL/PostProcessing.h"
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "Common/StringUtil.h"
|
||||
|
||||
#include "Core/Config/GraphicsSettings.h"
|
||||
|
||||
#include "VideoBackends/OGL/FramebufferManager.h"
|
||||
#include "VideoBackends/OGL/OGLTexture.h"
|
||||
#include "VideoBackends/OGL/ProgramShaderCache.h"
|
||||
#include "VideoBackends/OGL/SamplerCache.h"
|
||||
|
||||
#include "VideoCommon/VideoCommon.h"
|
||||
#include "VideoCommon/VideoConfig.h"
|
||||
|
||||
namespace OGL
|
||||
{
|
||||
static const char s_vertex_shader[] = "out vec2 uv0;\n"
|
||||
"uniform vec4 src_rect;\n"
|
||||
"void main(void) {\n"
|
||||
" vec2 rawpos = vec2(gl_VertexID&1, gl_VertexID&2);\n"
|
||||
" gl_Position = vec4(rawpos*2.0-1.0, 0.0, 1.0);\n"
|
||||
" uv0 = vec2(mix(src_rect.xy, src_rect.zw, rawpos));\n"
|
||||
"}\n";
|
||||
|
||||
OpenGLPostProcessing::OpenGLPostProcessing() : m_initialized(false)
|
||||
{
|
||||
CreateHeader();
|
||||
}
|
||||
|
||||
OpenGLPostProcessing::~OpenGLPostProcessing()
|
||||
{
|
||||
m_shader.Destroy();
|
||||
}
|
||||
|
||||
void OpenGLPostProcessing::BlitFromTexture(TargetRectangle src, TargetRectangle dst,
|
||||
int src_texture, int src_width, int src_height,
|
||||
int layer)
|
||||
{
|
||||
ApplyShader();
|
||||
|
||||
glViewport(dst.left, dst.bottom, dst.GetWidth(), dst.GetHeight());
|
||||
|
||||
ProgramShaderCache::BindVertexFormat(nullptr);
|
||||
|
||||
m_shader.Bind();
|
||||
|
||||
glUniform4f(m_uniform_resolution, (float)src_width, (float)src_height, 1.0f / (float)src_width,
|
||||
1.0f / (float)src_height);
|
||||
glUniform4f(m_uniform_src_rect, src.left / (float)src_width, src.top / (float)src_height,
|
||||
src.right / (float)src_width, src.bottom / (float)src_height);
|
||||
glUniform1ui(m_uniform_time, (GLuint)m_timer.GetTimeElapsed());
|
||||
glUniform1i(m_uniform_layer, layer);
|
||||
|
||||
if (m_config.IsDirty())
|
||||
{
|
||||
for (auto& it : m_config.GetOptions())
|
||||
{
|
||||
if (it.second.m_dirty)
|
||||
{
|
||||
switch (it.second.m_type)
|
||||
{
|
||||
case PostProcessingShaderConfiguration::ConfigurationOption::OptionType::OPTION_BOOL:
|
||||
glUniform1i(m_uniform_bindings[it.first], it.second.m_bool_value);
|
||||
break;
|
||||
case PostProcessingShaderConfiguration::ConfigurationOption::OptionType::OPTION_INTEGER:
|
||||
switch (it.second.m_integer_values.size())
|
||||
{
|
||||
case 1:
|
||||
glUniform1i(m_uniform_bindings[it.first], it.second.m_integer_values[0]);
|
||||
break;
|
||||
case 2:
|
||||
glUniform2i(m_uniform_bindings[it.first], it.second.m_integer_values[0],
|
||||
it.second.m_integer_values[1]);
|
||||
break;
|
||||
case 3:
|
||||
glUniform3i(m_uniform_bindings[it.first], it.second.m_integer_values[0],
|
||||
it.second.m_integer_values[1], it.second.m_integer_values[2]);
|
||||
break;
|
||||
case 4:
|
||||
glUniform4i(m_uniform_bindings[it.first], it.second.m_integer_values[0],
|
||||
it.second.m_integer_values[1], it.second.m_integer_values[2],
|
||||
it.second.m_integer_values[3]);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case PostProcessingShaderConfiguration::ConfigurationOption::OptionType::OPTION_FLOAT:
|
||||
switch (it.second.m_float_values.size())
|
||||
{
|
||||
case 1:
|
||||
glUniform1f(m_uniform_bindings[it.first], it.second.m_float_values[0]);
|
||||
break;
|
||||
case 2:
|
||||
glUniform2f(m_uniform_bindings[it.first], it.second.m_float_values[0],
|
||||
it.second.m_float_values[1]);
|
||||
break;
|
||||
case 3:
|
||||
glUniform3f(m_uniform_bindings[it.first], it.second.m_float_values[0],
|
||||
it.second.m_float_values[1], it.second.m_float_values[2]);
|
||||
break;
|
||||
case 4:
|
||||
glUniform4f(m_uniform_bindings[it.first], it.second.m_float_values[0],
|
||||
it.second.m_float_values[1], it.second.m_float_values[2],
|
||||
it.second.m_float_values[3]);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
it.second.m_dirty = false;
|
||||
}
|
||||
}
|
||||
m_config.SetDirty(false);
|
||||
}
|
||||
|
||||
glActiveTexture(GL_TEXTURE9);
|
||||
glBindTexture(GL_TEXTURE_2D_ARRAY, src_texture);
|
||||
g_sampler_cache->BindLinearSampler(9);
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
}
|
||||
|
||||
void OpenGLPostProcessing::ApplyShader()
|
||||
{
|
||||
// shader didn't changed
|
||||
if (m_initialized && m_config.GetShader() == g_ActiveConfig.sPostProcessingShader)
|
||||
return;
|
||||
|
||||
m_shader.Destroy();
|
||||
m_uniform_bindings.clear();
|
||||
|
||||
// load shader code
|
||||
std::string main_code = m_config.LoadShader();
|
||||
std::string options_code = LoadShaderOptions();
|
||||
std::string code = m_glsl_header + options_code + main_code;
|
||||
|
||||
// and compile it
|
||||
if (!ProgramShaderCache::CompileShader(m_shader, s_vertex_shader, code))
|
||||
{
|
||||
ERROR_LOG(VIDEO, "Failed to compile post-processing shader %s", m_config.GetShader().c_str());
|
||||
Config::SetCurrent(Config::GFX_ENHANCE_POST_SHADER, "");
|
||||
code = m_config.LoadShader();
|
||||
ProgramShaderCache::CompileShader(m_shader, s_vertex_shader, code);
|
||||
}
|
||||
|
||||
// read uniform locations
|
||||
m_uniform_resolution = glGetUniformLocation(m_shader.glprogid, "resolution");
|
||||
m_uniform_time = glGetUniformLocation(m_shader.glprogid, "time");
|
||||
m_uniform_src_rect = glGetUniformLocation(m_shader.glprogid, "src_rect");
|
||||
m_uniform_layer = glGetUniformLocation(m_shader.glprogid, "layer");
|
||||
|
||||
for (const auto& it : m_config.GetOptions())
|
||||
{
|
||||
std::string glsl_name = "options." + it.first;
|
||||
m_uniform_bindings[it.first] = glGetUniformLocation(m_shader.glprogid, glsl_name.c_str());
|
||||
}
|
||||
m_initialized = true;
|
||||
}
|
||||
|
||||
void OpenGLPostProcessing::CreateHeader()
|
||||
{
|
||||
m_glsl_header =
|
||||
// Required variables
|
||||
// Shouldn't be accessed directly by the PP shader
|
||||
// Texture sampler
|
||||
"SAMPLER_BINDING(8) uniform sampler2D samp8;\n"
|
||||
"SAMPLER_BINDING(9) uniform sampler2DArray samp9;\n"
|
||||
|
||||
// Output variable
|
||||
"out float4 ocol0;\n"
|
||||
// Input coordinates
|
||||
"in float2 uv0;\n"
|
||||
// Resolution
|
||||
"uniform float4 resolution;\n"
|
||||
// Time
|
||||
"uniform uint time;\n"
|
||||
// Layer
|
||||
"uniform int layer;\n"
|
||||
|
||||
// Interfacing functions
|
||||
"float4 Sample()\n"
|
||||
"{\n"
|
||||
"\treturn texture(samp9, float3(uv0, layer));\n"
|
||||
"}\n"
|
||||
|
||||
"float4 SampleLocation(float2 location)\n"
|
||||
"{\n"
|
||||
"\treturn texture(samp9, float3(location, layer));\n"
|
||||
"}\n"
|
||||
|
||||
"float4 SampleLayer(int layer)\n"
|
||||
"{\n"
|
||||
"\treturn texture(samp9, float3(uv0, layer));\n"
|
||||
"}\n"
|
||||
|
||||
"#define SampleOffset(offset) textureOffset(samp9, float3(uv0, layer), offset)\n"
|
||||
|
||||
"float2 GetResolution()\n"
|
||||
"{\n"
|
||||
"\treturn resolution.xy;\n"
|
||||
"}\n"
|
||||
|
||||
"float2 GetInvResolution()\n"
|
||||
"{\n"
|
||||
"\treturn resolution.zw;\n"
|
||||
"}\n"
|
||||
|
||||
"float2 GetCoordinates()\n"
|
||||
"{\n"
|
||||
"\treturn uv0;\n"
|
||||
"}\n"
|
||||
|
||||
"uint GetTime()\n"
|
||||
"{\n"
|
||||
"\treturn time;\n"
|
||||
"}\n"
|
||||
|
||||
"void SetOutput(float4 color)\n"
|
||||
"{\n"
|
||||
"\tocol0 = color;\n"
|
||||
"}\n"
|
||||
|
||||
"#define GetOption(x) (options.x)\n"
|
||||
"#define OptionEnabled(x) (options.x != 0)\n";
|
||||
}
|
||||
|
||||
std::string OpenGLPostProcessing::LoadShaderOptions()
|
||||
{
|
||||
m_uniform_bindings.clear();
|
||||
if (m_config.GetOptions().empty())
|
||||
return "";
|
||||
|
||||
std::string glsl_options = "struct Options\n{\n";
|
||||
|
||||
for (const auto& it : m_config.GetOptions())
|
||||
{
|
||||
if (it.second.m_type ==
|
||||
PostProcessingShaderConfiguration::ConfigurationOption::OptionType::OPTION_BOOL)
|
||||
{
|
||||
glsl_options += StringFromFormat("int %s;\n", it.first.c_str());
|
||||
}
|
||||
else if (it.second.m_type ==
|
||||
PostProcessingShaderConfiguration::ConfigurationOption::OptionType::OPTION_INTEGER)
|
||||
{
|
||||
u32 count = static_cast<u32>(it.second.m_integer_values.size());
|
||||
if (count == 1)
|
||||
glsl_options += StringFromFormat("int %s;\n", it.first.c_str());
|
||||
else
|
||||
glsl_options += StringFromFormat("int%d %s;\n", count, it.first.c_str());
|
||||
}
|
||||
else if (it.second.m_type ==
|
||||
PostProcessingShaderConfiguration::ConfigurationOption::OptionType::OPTION_FLOAT)
|
||||
{
|
||||
u32 count = static_cast<u32>(it.second.m_float_values.size());
|
||||
if (count == 1)
|
||||
glsl_options += StringFromFormat("float %s;\n", it.first.c_str());
|
||||
else
|
||||
glsl_options += StringFromFormat("float%d %s;\n", count, it.first.c_str());
|
||||
}
|
||||
|
||||
m_uniform_bindings[it.first] = 0;
|
||||
}
|
||||
|
||||
glsl_options += "};\n";
|
||||
glsl_options += "uniform Options options;\n";
|
||||
|
||||
return glsl_options;
|
||||
}
|
||||
|
||||
} // namespace OGL
|
@ -1,44 +0,0 @@
|
||||
// Copyright 2008 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "Common/GL/GLUtil.h"
|
||||
|
||||
#include "VideoBackends/OGL/ProgramShaderCache.h"
|
||||
|
||||
#include "VideoCommon/PostProcessing.h"
|
||||
#include "VideoCommon/VideoCommon.h"
|
||||
|
||||
namespace OGL
|
||||
{
|
||||
class OpenGLPostProcessing : public PostProcessingShaderImplementation
|
||||
{
|
||||
public:
|
||||
OpenGLPostProcessing();
|
||||
~OpenGLPostProcessing();
|
||||
|
||||
void BlitFromTexture(TargetRectangle src, TargetRectangle dst, int src_texture, int src_width,
|
||||
int src_height, int layer);
|
||||
void ApplyShader();
|
||||
|
||||
private:
|
||||
bool m_initialized;
|
||||
SHADER m_shader;
|
||||
GLuint m_uniform_resolution;
|
||||
GLuint m_uniform_src_rect;
|
||||
GLuint m_uniform_time;
|
||||
GLuint m_uniform_layer;
|
||||
std::string m_glsl_header;
|
||||
|
||||
std::unordered_map<std::string, GLuint> m_uniform_bindings;
|
||||
|
||||
void CreateHeader();
|
||||
std::string LoadShaderOptions();
|
||||
};
|
||||
|
||||
} // namespace
|
@ -4,6 +4,7 @@
|
||||
|
||||
#include "VideoBackends/OGL/ProgramShaderCache.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
@ -27,7 +28,6 @@
|
||||
#include "VideoBackends/OGL/VertexManager.h"
|
||||
|
||||
#include "VideoCommon/AsyncShaderCompiler.h"
|
||||
#include "VideoCommon/Debugger.h"
|
||||
#include "VideoCommon/DriverDetails.h"
|
||||
#include "VideoCommon/GeometryShaderManager.h"
|
||||
#include "VideoCommon/ImageWrite.h"
|
||||
@ -54,6 +54,7 @@ static GLuint CurrentProgram = 0;
|
||||
ProgramShaderCache::PipelineProgramMap ProgramShaderCache::s_pipeline_programs;
|
||||
std::mutex ProgramShaderCache::s_pipeline_program_lock;
|
||||
static std::string s_glsl_header = "";
|
||||
static std::atomic<u64> s_shader_counter{0};
|
||||
static thread_local bool s_is_shared_context = false;
|
||||
|
||||
static std::string GetGLSLVersionString()
|
||||
@ -109,13 +110,13 @@ void SHADER::SetProgramVariables()
|
||||
glUniformBlockBinding(glprogid, UBERBlock_id, 4);
|
||||
|
||||
// Bind Texture Samplers
|
||||
for (int a = 0; a < 10; ++a)
|
||||
for (int a = 0; a < 8; ++a)
|
||||
{
|
||||
std::string name = StringFromFormat(a < 8 ? "samp[%d]" : "samp%d", a);
|
||||
|
||||
// Still need to get sampler locations since we aren't binding them statically in the shaders
|
||||
int loc = glGetUniformLocation(glprogid, name.c_str());
|
||||
if (loc != -1)
|
||||
int loc = glGetUniformLocation(glprogid, StringFromFormat("samp[%d]", a).c_str());
|
||||
if (loc < 0)
|
||||
loc = glGetUniformLocation(glprogid, StringFromFormat("samp%d", a).c_str());
|
||||
if (loc >= 0)
|
||||
glUniform1i(loc, a);
|
||||
}
|
||||
|
||||
@ -191,21 +192,22 @@ bool PipelineProgramKey::operator!=(const PipelineProgramKey& rhs) const
|
||||
|
||||
bool PipelineProgramKey::operator==(const PipelineProgramKey& rhs) const
|
||||
{
|
||||
return std::tie(vertex_shader, geometry_shader, pixel_shader) ==
|
||||
std::tie(rhs.vertex_shader, rhs.geometry_shader, rhs.pixel_shader);
|
||||
return std::tie(vertex_shader_id, geometry_shader_id, pixel_shader_id) ==
|
||||
std::tie(rhs.vertex_shader_id, rhs.geometry_shader_id, rhs.pixel_shader_id);
|
||||
}
|
||||
|
||||
bool PipelineProgramKey::operator<(const PipelineProgramKey& rhs) const
|
||||
{
|
||||
return std::tie(vertex_shader, geometry_shader, pixel_shader) <
|
||||
std::tie(rhs.vertex_shader, rhs.geometry_shader, rhs.pixel_shader);
|
||||
return std::tie(vertex_shader_id, geometry_shader_id, pixel_shader_id) <
|
||||
std::tie(rhs.vertex_shader_id, rhs.geometry_shader_id, rhs.pixel_shader_id);
|
||||
}
|
||||
|
||||
std::size_t PipelineProgramKeyHash::operator()(const PipelineProgramKey& key) const
|
||||
{
|
||||
// We would really want std::hash_combine for this..
|
||||
std::hash<const void*> hasher;
|
||||
return hasher(key.vertex_shader) + hasher(key.geometry_shader) + hasher(key.pixel_shader);
|
||||
std::hash<u64> hasher;
|
||||
return hasher(key.vertex_shader_id) + hasher(key.geometry_shader_id) +
|
||||
hasher(key.pixel_shader_id);
|
||||
}
|
||||
|
||||
StreamBuffer* ProgramShaderCache::GetUniformBuffer()
|
||||
@ -218,13 +220,6 @@ u32 ProgramShaderCache::GetUniformBufferAlignment()
|
||||
return s_ubo_align;
|
||||
}
|
||||
|
||||
void ProgramShaderCache::InvalidateConstants()
|
||||
{
|
||||
VertexShaderManager::dirty = true;
|
||||
GeometryShaderManager::dirty = true;
|
||||
PixelShaderManager::dirty = true;
|
||||
}
|
||||
|
||||
void ProgramShaderCache::UploadConstants()
|
||||
{
|
||||
if (PixelShaderManager::dirty || VertexShaderManager::dirty || GeometryShaderManager::dirty)
|
||||
@ -574,7 +569,9 @@ const PipelineProgram* ProgramShaderCache::GetPipelineProgram(const GLVertexForm
|
||||
const OGLShader* geometry_shader,
|
||||
const OGLShader* pixel_shader)
|
||||
{
|
||||
PipelineProgramKey key = {vertex_shader, geometry_shader, pixel_shader};
|
||||
PipelineProgramKey key = {vertex_shader ? vertex_shader->GetID() : 0,
|
||||
geometry_shader ? geometry_shader->GetID() : 0,
|
||||
pixel_shader ? pixel_shader->GetID() : 0};
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(s_pipeline_program_lock);
|
||||
auto iter = s_pipeline_programs.find(key);
|
||||
@ -750,6 +747,7 @@ void ProgramShaderCache::CreateHeader()
|
||||
"%s\n"
|
||||
|
||||
// Silly differences
|
||||
"#define API_OPENGL 1\n"
|
||||
"#define float2 vec2\n"
|
||||
"#define float3 vec3\n"
|
||||
"#define float4 vec4\n"
|
||||
@ -759,8 +757,6 @@ void ProgramShaderCache::CreateHeader()
|
||||
"#define int2 ivec2\n"
|
||||
"#define int3 ivec3\n"
|
||||
"#define int4 ivec4\n"
|
||||
|
||||
// hlsl to glsl function translation
|
||||
"#define frac fract\n"
|
||||
"#define lerp mix\n"
|
||||
|
||||
@ -782,12 +778,17 @@ void ProgramShaderCache::CreateHeader()
|
||||
"#define FRAGMENT_OUTPUT_LOCATION_INDEXED(x, y)\n"
|
||||
"#define UBO_BINDING(packing, x) layout(packing, binding = x)\n"
|
||||
"#define SAMPLER_BINDING(x) layout(binding = x)\n"
|
||||
"#define SSBO_BINDING(x) layout(binding = x)\n" :
|
||||
"#define TEXEL_BUFFER_BINDING(x) layout(binding = x)\n"
|
||||
"#define SSBO_BINDING(x) layout(binding = x)\n"
|
||||
"#define IMAGE_BINDING(format, x) layout(format, binding = x)\n" :
|
||||
"#define ATTRIBUTE_LOCATION(x)\n"
|
||||
"#define FRAGMENT_OUTPUT_LOCATION(x)\n"
|
||||
"#define FRAGMENT_OUTPUT_LOCATION_INDEXED(x, y)\n"
|
||||
"#define UBO_BINDING(packing, x) layout(packing)\n"
|
||||
"#define SAMPLER_BINDING(x)\n",
|
||||
"#define SAMPLER_BINDING(x)\n"
|
||||
"#define TEXEL_BUFFER_BINDING(x)\n"
|
||||
"#define SSBO_BINDING(x)\n"
|
||||
"#define IMAGE_BINDING(format, x) layout(format)\n",
|
||||
// Input/output blocks are matched by name during program linking
|
||||
"#define VARYING_LOCATION(x)\n",
|
||||
!is_glsles && g_ActiveConfig.backend_info.bSupportsFragmentStoresAndAtomics ?
|
||||
@ -823,6 +824,11 @@ void ProgramShaderCache::CreateHeader()
|
||||
v >= GlslEs310 ? "precision highp image2DArray;" : "");
|
||||
}
|
||||
|
||||
u64 ProgramShaderCache::GenerateShaderID()
|
||||
{
|
||||
return s_shader_counter++;
|
||||
}
|
||||
|
||||
bool SharedContextAsyncShaderCompiler::WorkerThreadInitMainThread(void** param)
|
||||
{
|
||||
std::unique_ptr<GLContext> context =
|
||||
|
@ -44,9 +44,9 @@ struct SHADER
|
||||
|
||||
struct PipelineProgramKey
|
||||
{
|
||||
const OGLShader* vertex_shader;
|
||||
const OGLShader* geometry_shader;
|
||||
const OGLShader* pixel_shader;
|
||||
u64 vertex_shader_id;
|
||||
u64 geometry_shader_id;
|
||||
u64 pixel_shader_id;
|
||||
|
||||
bool operator==(const PipelineProgramKey& rhs) const;
|
||||
bool operator!=(const PipelineProgramKey& rhs) const;
|
||||
@ -82,7 +82,6 @@ public:
|
||||
const std::string& gcode);
|
||||
static StreamBuffer* GetUniformBuffer();
|
||||
static u32 GetUniformBufferAlignment();
|
||||
static void InvalidateConstants();
|
||||
static void UploadConstants();
|
||||
static void UploadConstants(const void* data, u32 data_size);
|
||||
|
||||
@ -90,6 +89,14 @@ public:
|
||||
static void Shutdown();
|
||||
static void CreateHeader();
|
||||
|
||||
// This counter increments with each shader object allocated, in order to give it a unique ID.
|
||||
// Since the shaders can be destroyed after a pipeline is created, we can't use the shader pointer
|
||||
// as a key for GL programs. For the same reason, we can't use the GL objects either. This ID is
|
||||
// guaranteed to be unique for the emulation session, even if the memory allocator or GL driver
|
||||
// re-uses pointers, therefore we won't have any collisions where the shaders attached to a
|
||||
// pipeline do not match the pipeline configuration.
|
||||
static u64 GenerateShaderID();
|
||||
|
||||
static const PipelineProgram* GetPipelineProgram(const GLVertexFormat* vertex_format,
|
||||
const OGLShader* vertex_shader,
|
||||
const OGLShader* geometry_shader,
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -11,12 +11,11 @@
|
||||
#include "Common/GL/GLExtensions/GLExtensions.h"
|
||||
#include "VideoCommon/RenderBase.h"
|
||||
|
||||
struct XFBSourceBase;
|
||||
|
||||
namespace OGL
|
||||
{
|
||||
class OGLFramebuffer;
|
||||
class OGLPipeline;
|
||||
void ClearEFBCache();
|
||||
class OGLTexture;
|
||||
|
||||
enum GlslVersion
|
||||
{
|
||||
@ -86,6 +85,8 @@ public:
|
||||
Renderer(std::unique_ptr<GLContext> main_gl_context, float backbuffer_scale);
|
||||
~Renderer() override;
|
||||
|
||||
static Renderer* GetInstance() { return static_cast<Renderer*>(g_renderer.get()); }
|
||||
|
||||
bool IsHeadless() const override;
|
||||
|
||||
bool Initialize() override;
|
||||
@ -98,73 +99,80 @@ public:
|
||||
size_t length) override;
|
||||
std::unique_ptr<AbstractShader> CreateShaderFromBinary(ShaderStage stage, const void* data,
|
||||
size_t length) override;
|
||||
std::unique_ptr<NativeVertexFormat>
|
||||
CreateNativeVertexFormat(const PortableVertexDeclaration& vtx_decl) override;
|
||||
std::unique_ptr<AbstractPipeline> CreatePipeline(const AbstractPipelineConfig& config) override;
|
||||
std::unique_ptr<AbstractFramebuffer>
|
||||
CreateFramebuffer(const AbstractTexture* color_attachment,
|
||||
const AbstractTexture* depth_attachment) override;
|
||||
CreateFramebuffer(AbstractTexture* color_attachment, AbstractTexture* depth_attachment) override;
|
||||
|
||||
void SetPipeline(const AbstractPipeline* pipeline) override;
|
||||
void SetFramebuffer(const AbstractFramebuffer* framebuffer) override;
|
||||
void SetAndDiscardFramebuffer(const AbstractFramebuffer* framebuffer) override;
|
||||
void SetAndClearFramebuffer(const AbstractFramebuffer* framebuffer,
|
||||
const ClearColor& color_value = {},
|
||||
void SetFramebuffer(AbstractFramebuffer* framebuffer) override;
|
||||
void SetAndDiscardFramebuffer(AbstractFramebuffer* framebuffer) override;
|
||||
void SetAndClearFramebuffer(AbstractFramebuffer* framebuffer, const ClearColor& color_value = {},
|
||||
float depth_value = 0.0f) override;
|
||||
void SetScissorRect(const MathUtil::Rectangle<int>& rc) override;
|
||||
void SetTexture(u32 index, const AbstractTexture* texture) override;
|
||||
void SetSamplerState(u32 index, const SamplerState& state) override;
|
||||
void SetComputeImageTexture(AbstractTexture* texture, bool read, bool write) override;
|
||||
void UnbindTexture(const AbstractTexture* texture) override;
|
||||
void SetInterlacingMode() override;
|
||||
void SetViewport(float x, float y, float width, float height, float near_depth,
|
||||
float far_depth) override;
|
||||
void Draw(u32 base_vertex, u32 num_vertices) override;
|
||||
void DrawIndexed(u32 base_index, u32 num_indices, u32 base_vertex) override;
|
||||
void DispatchComputeShader(const AbstractShader* shader, u32 groups_x, u32 groups_y,
|
||||
u32 groups_z) override;
|
||||
void BindBackbuffer(const ClearColor& clear_color = {}) override;
|
||||
void PresentBackbuffer() override;
|
||||
|
||||
u32 AccessEFB(EFBAccessType type, u32 x, u32 y, u32 poke_data) override;
|
||||
void PokeEFB(EFBAccessType type, const EfbPokeData* points, size_t num_points) override;
|
||||
|
||||
u16 BBoxRead(int index) override;
|
||||
void BBoxWrite(int index, u16 value) override;
|
||||
|
||||
void ResetAPIState() override;
|
||||
void RestoreAPIState() override;
|
||||
|
||||
TargetRectangle ConvertEFBRectangle(const EFBRectangle& rc) override;
|
||||
void BeginUtilityDrawing() override;
|
||||
void EndUtilityDrawing() override;
|
||||
|
||||
void Flush() override;
|
||||
void WaitForGPUIdle() override;
|
||||
void RenderXFBToScreen(const AbstractTexture* texture, const EFBRectangle& rc) override;
|
||||
void OnConfigChanged(u32 bits) override;
|
||||
|
||||
void ClearScreen(const EFBRectangle& rc, bool colorEnable, bool alphaEnable, bool zEnable,
|
||||
u32 color, u32 z) override;
|
||||
|
||||
void ReinterpretPixelData(unsigned int convtype) override;
|
||||
|
||||
std::unique_ptr<VideoCommon::AsyncShaderCompiler> CreateAsyncShaderCompiler() override;
|
||||
|
||||
// Only call methods from this on the GPU thread.
|
||||
GLContext* GetMainGLContext() const { return m_main_gl_context.get(); }
|
||||
bool IsGLES() const { return m_main_gl_context->IsGLES(); }
|
||||
|
||||
const OGLPipeline* GetCurrentGraphicsPipeline() const { return m_graphics_pipeline; }
|
||||
// Invalidates a cached texture binding. Required for texel buffers when they borrow the units.
|
||||
void InvalidateTextureBinding(u32 index) { m_bound_textures[index] = nullptr; }
|
||||
|
||||
// The shared framebuffer exists for copying textures when extensions are not available. It is
|
||||
// slower, but the only way to do these things otherwise.
|
||||
GLuint GetSharedReadFramebuffer() const { return m_shared_read_framebuffer; }
|
||||
GLuint GetSharedDrawFramebuffer() const { return m_shared_draw_framebuffer; }
|
||||
void BindSharedReadFramebuffer();
|
||||
void BindSharedDrawFramebuffer();
|
||||
|
||||
// Restores FBO binding after it's been changed.
|
||||
void RestoreFramebufferBinding();
|
||||
|
||||
private:
|
||||
void UpdateEFBCache(EFBAccessType type, u32 cacheRectIdx, const EFBRectangle& efbPixelRc,
|
||||
const TargetRectangle& targetPixelRc, const void* data);
|
||||
|
||||
void CheckForSurfaceChange();
|
||||
void CheckForSurfaceResize();
|
||||
|
||||
void ApplyBlendingState(const BlendingState state, bool force = false);
|
||||
void ApplyRasterizationState(const RasterizationState state, bool force = false);
|
||||
void ApplyDepthState(const DepthState state, bool force = false);
|
||||
void ApplyRasterizationState(const RasterizationState state);
|
||||
void ApplyDepthState(const DepthState state);
|
||||
void ApplyBlendingState(const BlendingState state);
|
||||
|
||||
std::unique_ptr<GLContext> m_main_gl_context;
|
||||
std::array<const AbstractTexture*, 8> m_bound_textures{};
|
||||
const OGLPipeline* m_graphics_pipeline = nullptr;
|
||||
std::unique_ptr<OGLFramebuffer> m_system_framebuffer;
|
||||
std::array<const OGLTexture*, 8> m_bound_textures{};
|
||||
AbstractTexture* m_bound_image_texture = nullptr;
|
||||
RasterizationState m_current_rasterization_state;
|
||||
DepthState m_current_depth_state;
|
||||
BlendingState m_current_blend_state;
|
||||
GLuint m_shared_read_framebuffer = 0;
|
||||
GLuint m_shared_draw_framebuffer = 0;
|
||||
};
|
||||
} // namespace OGL
|
||||
|
@ -19,6 +19,8 @@ public:
|
||||
static std::unique_ptr<StreamBuffer> Create(u32 type, u32 size);
|
||||
virtual ~StreamBuffer();
|
||||
|
||||
u32 GetGLBufferId() const { return m_buffer; }
|
||||
u32 GetSize() const { return m_size; }
|
||||
u32 GetCurrentOffset() const { return m_iterator; }
|
||||
|
||||
/* This mapping function will return a pair of:
|
||||
@ -64,4 +66,4 @@ private:
|
||||
|
||||
std::array<GLsync, SYNC_POINTS> m_fences{};
|
||||
};
|
||||
}
|
||||
} // namespace OGL
|
||||
|
@ -1,574 +0,0 @@
|
||||
// Copyright 2008 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "Common/Assert.h"
|
||||
#include "Common/MsgHandler.h"
|
||||
#include "Common/StringUtil.h"
|
||||
|
||||
#include "VideoBackends/OGL/FramebufferManager.h"
|
||||
#include "VideoBackends/OGL/GPUTimer.h"
|
||||
#include "VideoBackends/OGL/OGLTexture.h"
|
||||
#include "VideoBackends/OGL/ProgramShaderCache.h"
|
||||
#include "VideoBackends/OGL/Render.h"
|
||||
#include "VideoBackends/OGL/SamplerCache.h"
|
||||
#include "VideoBackends/OGL/StreamBuffer.h"
|
||||
#include "VideoBackends/OGL/TextureCache.h"
|
||||
#include "VideoBackends/OGL/TextureConverter.h"
|
||||
|
||||
#include "VideoCommon/ImageWrite.h"
|
||||
#include "VideoCommon/TextureConversionShader.h"
|
||||
#include "VideoCommon/TextureConverterShaderGen.h"
|
||||
#include "VideoCommon/TextureDecoder.h"
|
||||
#include "VideoCommon/VideoCommon.h"
|
||||
#include "VideoCommon/VideoConfig.h"
|
||||
|
||||
namespace OGL
|
||||
{
|
||||
constexpr const char GLSL_PROGRAM_VS[] = R"GLSL(
|
||||
out vec3 %c_uv0;
|
||||
SAMPLER_BINDING(9) uniform sampler2DArray samp9;
|
||||
uniform vec4 copy_position; // left, top, right, bottom
|
||||
|
||||
void main()
|
||||
{
|
||||
vec2 rawpos = vec2(gl_VertexID&1, gl_VertexID&2);
|
||||
%c_uv0 = vec3(mix(copy_position.xy, copy_position.zw, rawpos) / vec2(textureSize(samp9, 0).xy), 0.0);
|
||||
gl_Position = vec4(rawpos*2.0-1.0, 0.0, 1.0);
|
||||
}
|
||||
)GLSL";
|
||||
|
||||
constexpr const char GLSL_PROGRAM_GS[] = R"GLSL(
|
||||
layout(triangles) in;
|
||||
layout(triangle_strip, max_vertices = 6) out;
|
||||
in vec3 v_uv0[3];
|
||||
out vec3 f_uv0;
|
||||
SAMPLER_BINDING(9) uniform sampler2DArray samp9;
|
||||
|
||||
void main()
|
||||
{
|
||||
int layers = textureSize(samp9, 0).z;
|
||||
for (int layer = 0; layer < layers; ++layer) {
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
f_uv0 = vec3(v_uv0[i].xy, layer);
|
||||
gl_Position = gl_in[i].gl_Position;
|
||||
gl_Layer = layer;
|
||||
EmitVertex();
|
||||
}
|
||||
}
|
||||
EndPrimitive();
|
||||
}
|
||||
)GLSL";
|
||||
|
||||
constexpr const char GLSL_COLOR_COPY_FS[] = R"GLSL(
|
||||
SAMPLER_BINDING(9) uniform sampler2DArray samp9;
|
||||
in vec3 f_uv0;
|
||||
out vec4 ocol0;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 texcol = texture(samp9, f_uv0);
|
||||
ocol0 = texcol;
|
||||
}
|
||||
)GLSL";
|
||||
|
||||
constexpr const char GLSL_PALETTE_FS[] = R"GLSL(
|
||||
uniform int texture_buffer_offset;
|
||||
uniform float multiplier;
|
||||
SAMPLER_BINDING(9) uniform sampler2DArray samp9;
|
||||
SAMPLER_BINDING(10) uniform usamplerBuffer samp10;
|
||||
|
||||
in vec3 f_uv0;
|
||||
out vec4 ocol0;
|
||||
|
||||
int Convert3To8(int v)
|
||||
{
|
||||
// Swizzle bits: 00000123 -> 12312312
|
||||
return (v << 5) | (v << 2) | (v >> 1);
|
||||
}
|
||||
|
||||
int Convert4To8(int v)
|
||||
{
|
||||
// Swizzle bits: 00001234 -> 12341234
|
||||
return (v << 4) | v;
|
||||
}
|
||||
|
||||
int Convert5To8(int v)
|
||||
{
|
||||
// Swizzle bits: 00012345 -> 12345123
|
||||
return (v << 3) | (v >> 2);
|
||||
}
|
||||
|
||||
int Convert6To8(int v)
|
||||
{
|
||||
// Swizzle bits: 00123456 -> 12345612
|
||||
return (v << 2) | (v >> 4);
|
||||
}
|
||||
|
||||
float4 DecodePixel_RGB5A3(int val)
|
||||
{
|
||||
int r,g,b,a;
|
||||
if ((val&0x8000) > 0)
|
||||
{
|
||||
r=Convert5To8((val>>10) & 0x1f);
|
||||
g=Convert5To8((val>>5 ) & 0x1f);
|
||||
b=Convert5To8((val ) & 0x1f);
|
||||
a=0xFF;
|
||||
}
|
||||
else
|
||||
{
|
||||
a=Convert3To8((val>>12) & 0x7);
|
||||
r=Convert4To8((val>>8 ) & 0xf);
|
||||
g=Convert4To8((val>>4 ) & 0xf);
|
||||
b=Convert4To8((val ) & 0xf);
|
||||
}
|
||||
return float4(r, g, b, a) / 255.0;
|
||||
}
|
||||
|
||||
float4 DecodePixel_RGB565(int val)
|
||||
{
|
||||
int r, g, b, a;
|
||||
r = Convert5To8((val >> 11) & 0x1f);
|
||||
g = Convert6To8((val >> 5) & 0x3f);
|
||||
b = Convert5To8((val) & 0x1f);
|
||||
a = 0xFF;
|
||||
return float4(r, g, b, a) / 255.0;
|
||||
}
|
||||
|
||||
float4 DecodePixel_IA8(int val)
|
||||
{
|
||||
int i = val & 0xFF;
|
||||
int a = val >> 8;
|
||||
return float4(i, i, i, a) / 255.0;
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
int src = int(round(texture(samp9, f_uv0).r * multiplier));
|
||||
src = int(texelFetch(samp10, src + texture_buffer_offset).r);
|
||||
src = ((src << 8) & 0xFF00) | (src >> 8);
|
||||
ocol0 = DecodePixel_%s(src);
|
||||
}
|
||||
)GLSL";
|
||||
|
||||
//#define TIME_TEXTURE_DECODING 1
|
||||
|
||||
void TextureCache::CopyEFB(AbstractStagingTexture* dst, const EFBCopyParams& params,
|
||||
u32 native_width, u32 bytes_per_row, u32 num_blocks_y, u32 memory_stride,
|
||||
const EFBRectangle& src_rect, bool scale_by_half, float y_scale,
|
||||
float gamma, bool clamp_top, bool clamp_bottom,
|
||||
const CopyFilterCoefficientArray& filter_coefficients)
|
||||
{
|
||||
// Flip top/bottom due to lower-left coordinate system.
|
||||
float clamp_top_val =
|
||||
clamp_bottom ? (1.0f - src_rect.bottom / static_cast<float>(EFB_HEIGHT)) : 0.0f;
|
||||
float clamp_bottom_val =
|
||||
clamp_top ? (1.0f - src_rect.top / static_cast<float>(EFB_HEIGHT)) : 1.0f;
|
||||
TextureConverter::EncodeToRamFromTexture(dst, params, native_width, bytes_per_row, num_blocks_y,
|
||||
memory_stride, src_rect, scale_by_half, y_scale, gamma,
|
||||
clamp_top_val, clamp_bottom_val, filter_coefficients);
|
||||
}
|
||||
|
||||
TextureCache::TextureCache()
|
||||
{
|
||||
CompileShaders();
|
||||
|
||||
if (g_ActiveConfig.backend_info.bSupportsPaletteConversion)
|
||||
{
|
||||
s32 buffer_size_mb = (g_ActiveConfig.backend_info.bSupportsGPUTextureDecoding ? 32 : 1);
|
||||
s32 buffer_size = buffer_size_mb * 1024 * 1024;
|
||||
s32 max_buffer_size = 0;
|
||||
|
||||
// The minimum MAX_TEXTURE_BUFFER_SIZE that the spec mandates is 65KB, we are asking for a 1MB
|
||||
// buffer here. This buffer is also used as storage for undecoded textures when compute shader
|
||||
// texture decoding is enabled, in which case the requested size is 32MB.
|
||||
glGetIntegerv(GL_MAX_TEXTURE_BUFFER_SIZE, &max_buffer_size);
|
||||
|
||||
// Clamp the buffer size to the maximum size that the driver supports.
|
||||
buffer_size = std::min(buffer_size, max_buffer_size);
|
||||
|
||||
m_palette_stream_buffer = StreamBuffer::Create(GL_TEXTURE_BUFFER, buffer_size);
|
||||
glGenTextures(1, &m_palette_resolv_texture);
|
||||
glBindTexture(GL_TEXTURE_BUFFER, m_palette_resolv_texture);
|
||||
glTexBuffer(GL_TEXTURE_BUFFER, GL_R16UI, m_palette_stream_buffer->m_buffer);
|
||||
|
||||
if (g_ActiveConfig.backend_info.bSupportsGPUTextureDecoding)
|
||||
CreateTextureDecodingResources();
|
||||
}
|
||||
}
|
||||
|
||||
TextureCache::~TextureCache()
|
||||
{
|
||||
DeleteShaders();
|
||||
if (g_ActiveConfig.backend_info.bSupportsGPUTextureDecoding)
|
||||
DestroyTextureDecodingResources();
|
||||
|
||||
if (g_ActiveConfig.backend_info.bSupportsPaletteConversion)
|
||||
{
|
||||
glDeleteTextures(1, &m_palette_resolv_texture);
|
||||
}
|
||||
}
|
||||
|
||||
TextureCache* TextureCache::GetInstance()
|
||||
{
|
||||
return static_cast<TextureCache*>(g_texture_cache.get());
|
||||
}
|
||||
|
||||
const SHADER& TextureCache::GetColorCopyProgram() const
|
||||
{
|
||||
return m_colorCopyProgram;
|
||||
}
|
||||
|
||||
GLuint TextureCache::GetColorCopyPositionUniform() const
|
||||
{
|
||||
return m_colorCopyPositionUniform;
|
||||
}
|
||||
|
||||
bool TextureCache::CompilePaletteShader(TLUTFormat tlutfmt, const std::string& vcode,
|
||||
const std::string& pcode, const std::string& gcode)
|
||||
{
|
||||
ASSERT(IsValidTLUTFormat(tlutfmt));
|
||||
PaletteShader& shader = m_palette_shaders[static_cast<int>(tlutfmt)];
|
||||
|
||||
if (!ProgramShaderCache::CompileShader(shader.shader, vcode, pcode, gcode))
|
||||
return false;
|
||||
|
||||
shader.buffer_offset_uniform =
|
||||
glGetUniformLocation(shader.shader.glprogid, "texture_buffer_offset");
|
||||
shader.multiplier_uniform = glGetUniformLocation(shader.shader.glprogid, "multiplier");
|
||||
shader.copy_position_uniform = glGetUniformLocation(shader.shader.glprogid, "copy_position");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TextureCache::CompileShaders()
|
||||
{
|
||||
std::string geo_program = "";
|
||||
char prefix = 'f';
|
||||
if (g_ActiveConfig.stereo_mode != StereoMode::Off)
|
||||
{
|
||||
geo_program = GLSL_PROGRAM_GS;
|
||||
prefix = 'v';
|
||||
}
|
||||
|
||||
if (!ProgramShaderCache::CompileShader(m_colorCopyProgram,
|
||||
StringFromFormat(GLSL_PROGRAM_VS, prefix, prefix),
|
||||
GLSL_COLOR_COPY_FS, geo_program))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_colorCopyPositionUniform = glGetUniformLocation(m_colorCopyProgram.glprogid, "copy_position");
|
||||
|
||||
if (g_ActiveConfig.backend_info.bSupportsPaletteConversion)
|
||||
{
|
||||
if (!CompilePaletteShader(TLUTFormat::IA8, StringFromFormat(GLSL_PROGRAM_VS, prefix, prefix),
|
||||
StringFromFormat(GLSL_PALETTE_FS, "IA8"), geo_program))
|
||||
return false;
|
||||
|
||||
if (!CompilePaletteShader(TLUTFormat::RGB565, StringFromFormat(GLSL_PROGRAM_VS, prefix, prefix),
|
||||
StringFromFormat(GLSL_PALETTE_FS, "RGB565"), geo_program))
|
||||
return false;
|
||||
|
||||
if (!CompilePaletteShader(TLUTFormat::RGB5A3, StringFromFormat(GLSL_PROGRAM_VS, prefix, prefix),
|
||||
StringFromFormat(GLSL_PALETTE_FS, "RGB5A3"), geo_program))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void TextureCache::DeleteShaders()
|
||||
{
|
||||
for (auto& it : m_efb_copy_programs)
|
||||
it.second.shader.Destroy();
|
||||
m_efb_copy_programs.clear();
|
||||
|
||||
if (g_ActiveConfig.backend_info.bSupportsPaletteConversion)
|
||||
for (auto& shader : m_palette_shaders)
|
||||
shader.shader.Destroy();
|
||||
}
|
||||
|
||||
void TextureCache::ConvertTexture(TCacheEntry* destination, TCacheEntry* source,
|
||||
const void* palette, TLUTFormat tlutfmt)
|
||||
{
|
||||
if (!g_ActiveConfig.backend_info.bSupportsPaletteConversion)
|
||||
return;
|
||||
|
||||
ASSERT(IsValidTLUTFormat(tlutfmt));
|
||||
const PaletteShader& palette_shader = m_palette_shaders[static_cast<int>(tlutfmt)];
|
||||
|
||||
g_renderer->ResetAPIState();
|
||||
|
||||
OGLTexture* source_texture = static_cast<OGLTexture*>(source->texture.get());
|
||||
OGLTexture* destination_texture = static_cast<OGLTexture*>(destination->texture.get());
|
||||
|
||||
glActiveTexture(GL_TEXTURE9);
|
||||
glBindTexture(GL_TEXTURE_2D_ARRAY, source_texture->GetRawTexIdentifier());
|
||||
g_sampler_cache->BindNearestSampler(9);
|
||||
|
||||
FramebufferManager::SetFramebuffer(destination_texture->GetFramebuffer());
|
||||
glViewport(0, 0, destination->GetWidth(), destination->GetHeight());
|
||||
palette_shader.shader.Bind();
|
||||
|
||||
// C14 textures are currently unsupported
|
||||
int size = source->format == TextureFormat::I4 ? 32 : 512;
|
||||
auto buffer = m_palette_stream_buffer->Map(size);
|
||||
memcpy(buffer.first, palette, size);
|
||||
m_palette_stream_buffer->Unmap(size);
|
||||
glUniform1i(palette_shader.buffer_offset_uniform, buffer.second / 2);
|
||||
glUniform1f(palette_shader.multiplier_uniform,
|
||||
source->format == TextureFormat::I4 ? 15.0f : 255.0f);
|
||||
glUniform4f(palette_shader.copy_position_uniform, 0.0f, 0.0f,
|
||||
static_cast<float>(source->GetWidth()), static_cast<float>(source->GetHeight()));
|
||||
|
||||
glActiveTexture(GL_TEXTURE10);
|
||||
glBindTexture(GL_TEXTURE_BUFFER, m_palette_resolv_texture);
|
||||
g_sampler_cache->BindNearestSampler(10);
|
||||
|
||||
ProgramShaderCache::BindVertexFormat(nullptr);
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
|
||||
g_renderer->RestoreAPIState();
|
||||
}
|
||||
|
||||
static const std::string decoding_vertex_shader = R"(
|
||||
void main()
|
||||
{
|
||||
vec2 rawpos = vec2(gl_VertexID&1, gl_VertexID&2);
|
||||
gl_Position = vec4(rawpos*2.0-1.0, 0.0, 1.0);
|
||||
}
|
||||
)";
|
||||
|
||||
void TextureCache::CreateTextureDecodingResources()
|
||||
{
|
||||
static const GLenum gl_view_types[TextureConversionShaderTiled::BUFFER_FORMAT_COUNT] = {
|
||||
GL_R8UI, // BUFFER_FORMAT_R8_UINT
|
||||
GL_R16UI, // BUFFER_FORMAT_R16_UINT
|
||||
GL_RG32UI, // BUFFER_FORMAT_R32G32_UINT
|
||||
GL_RGBA8UI, // BUFFER_FORMAT_RGBA8_UINT
|
||||
};
|
||||
|
||||
glGenTextures(TextureConversionShaderTiled::BUFFER_FORMAT_COUNT,
|
||||
m_texture_decoding_buffer_views.data());
|
||||
for (size_t i = 0; i < TextureConversionShaderTiled::BUFFER_FORMAT_COUNT; i++)
|
||||
{
|
||||
glBindTexture(GL_TEXTURE_BUFFER, m_texture_decoding_buffer_views[i]);
|
||||
glTexBuffer(GL_TEXTURE_BUFFER, gl_view_types[i], m_palette_stream_buffer->m_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
void TextureCache::DestroyTextureDecodingResources()
|
||||
{
|
||||
glDeleteTextures(TextureConversionShaderTiled::BUFFER_FORMAT_COUNT,
|
||||
m_texture_decoding_buffer_views.data());
|
||||
m_texture_decoding_buffer_views.fill(0);
|
||||
m_texture_decoding_program_info.clear();
|
||||
}
|
||||
|
||||
bool TextureCache::SupportsGPUTextureDecode(TextureFormat format, TLUTFormat palette_format)
|
||||
{
|
||||
auto key = std::make_pair(static_cast<u32>(format), static_cast<u32>(palette_format));
|
||||
auto iter = m_texture_decoding_program_info.find(key);
|
||||
if (iter != m_texture_decoding_program_info.end())
|
||||
return iter->second.valid;
|
||||
|
||||
TextureDecodingProgramInfo info;
|
||||
info.base_info = TextureConversionShaderTiled::GetDecodingShaderInfo(format);
|
||||
if (!info.base_info)
|
||||
{
|
||||
m_texture_decoding_program_info.emplace(key, info);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string shader_source =
|
||||
TextureConversionShaderTiled::GenerateDecodingShader(format, palette_format, APIType::OpenGL);
|
||||
if (shader_source.empty())
|
||||
{
|
||||
m_texture_decoding_program_info.emplace(key, info);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ProgramShaderCache::CompileComputeShader(info.program, shader_source))
|
||||
{
|
||||
m_texture_decoding_program_info.emplace(key, info);
|
||||
return false;
|
||||
}
|
||||
|
||||
info.uniform_dst_size = glGetUniformLocation(info.program.glprogid, "u_dst_size");
|
||||
info.uniform_src_size = glGetUniformLocation(info.program.glprogid, "u_src_size");
|
||||
info.uniform_src_offset = glGetUniformLocation(info.program.glprogid, "u_src_offset");
|
||||
info.uniform_src_row_stride = glGetUniformLocation(info.program.glprogid, "u_src_row_stride");
|
||||
info.uniform_palette_offset = glGetUniformLocation(info.program.glprogid, "u_palette_offset");
|
||||
info.valid = true;
|
||||
m_texture_decoding_program_info.emplace(key, info);
|
||||
return true;
|
||||
}
|
||||
|
||||
void TextureCache::DecodeTextureOnGPU(TCacheEntry* entry, u32 dst_level, const u8* data,
|
||||
size_t data_size, TextureFormat format, u32 width, u32 height,
|
||||
u32 aligned_width, u32 aligned_height, u32 row_stride,
|
||||
const u8* palette, TLUTFormat palette_format)
|
||||
{
|
||||
auto key = std::make_pair(static_cast<u32>(format), static_cast<u32>(palette_format));
|
||||
auto iter = m_texture_decoding_program_info.find(key);
|
||||
if (iter == m_texture_decoding_program_info.end())
|
||||
return;
|
||||
|
||||
#ifdef TIME_TEXTURE_DECODING
|
||||
GPUTimer timer;
|
||||
#endif
|
||||
|
||||
// Copy to GPU-visible buffer, aligned to the data type.
|
||||
auto info = iter->second;
|
||||
u32 bytes_per_buffer_elem =
|
||||
TextureConversionShaderTiled::GetBytesPerBufferElement(info.base_info->buffer_format);
|
||||
|
||||
// Only copy palette if it is required.
|
||||
bool has_palette = info.base_info->palette_size > 0;
|
||||
u32 total_upload_size = static_cast<u32>(data_size);
|
||||
u32 palette_offset = total_upload_size;
|
||||
if (has_palette)
|
||||
{
|
||||
// Align to u16.
|
||||
if ((total_upload_size % sizeof(u16)) != 0)
|
||||
{
|
||||
total_upload_size++;
|
||||
palette_offset++;
|
||||
}
|
||||
|
||||
total_upload_size += info.base_info->palette_size;
|
||||
}
|
||||
|
||||
// Allocate space in stream buffer, and copy texture + palette across.
|
||||
auto buffer = m_palette_stream_buffer->Map(total_upload_size, bytes_per_buffer_elem);
|
||||
memcpy(buffer.first, data, data_size);
|
||||
if (has_palette)
|
||||
memcpy(buffer.first + palette_offset, palette, info.base_info->palette_size);
|
||||
m_palette_stream_buffer->Unmap(total_upload_size);
|
||||
|
||||
info.program.Bind();
|
||||
|
||||
// Calculate stride in buffer elements
|
||||
u32 row_stride_in_elements = row_stride / bytes_per_buffer_elem;
|
||||
u32 offset_in_elements = buffer.second / bytes_per_buffer_elem;
|
||||
u32 palette_offset_in_elements = (buffer.second + palette_offset) / sizeof(u16);
|
||||
if (info.uniform_dst_size >= 0)
|
||||
glUniform2ui(info.uniform_dst_size, width, height);
|
||||
if (info.uniform_src_size >= 0)
|
||||
glUniform2ui(info.uniform_src_size, aligned_width, aligned_height);
|
||||
if (info.uniform_src_offset >= 0)
|
||||
glUniform1ui(info.uniform_src_offset, offset_in_elements);
|
||||
if (info.uniform_src_row_stride >= 0)
|
||||
glUniform1ui(info.uniform_src_row_stride, row_stride_in_elements);
|
||||
if (info.uniform_palette_offset >= 0)
|
||||
glUniform1ui(info.uniform_palette_offset, palette_offset_in_elements);
|
||||
|
||||
glActiveTexture(GL_TEXTURE9);
|
||||
glBindTexture(GL_TEXTURE_BUFFER, m_texture_decoding_buffer_views[info.base_info->buffer_format]);
|
||||
|
||||
if (has_palette)
|
||||
{
|
||||
// Use an R16UI view for the palette.
|
||||
glActiveTexture(GL_TEXTURE10);
|
||||
glBindTexture(GL_TEXTURE_BUFFER, m_palette_resolv_texture);
|
||||
}
|
||||
|
||||
auto dispatch_groups =
|
||||
TextureConversionShaderTiled::GetDispatchCount(info.base_info, aligned_width, aligned_height);
|
||||
glBindImageTexture(0, static_cast<OGLTexture*>(entry->texture.get())->GetRawTexIdentifier(),
|
||||
dst_level, GL_TRUE, 0, GL_WRITE_ONLY, GL_RGBA8);
|
||||
glDispatchCompute(dispatch_groups.first, dispatch_groups.second, 1);
|
||||
glMemoryBarrier(GL_TEXTURE_UPDATE_BARRIER_BIT);
|
||||
|
||||
#ifdef TIME_TEXTURE_DECODING
|
||||
WARN_LOG(VIDEO, "Decode texture format %u size %ux%u took %.4fms", static_cast<u32>(format),
|
||||
width, height, timer.GetTimeMilliseconds());
|
||||
#endif
|
||||
}
|
||||
|
||||
void TextureCache::CopyEFBToCacheEntry(TCacheEntry* entry, bool is_depth_copy,
|
||||
const EFBRectangle& src_rect, bool scale_by_half,
|
||||
EFBCopyFormat dst_format, bool is_intensity, float gamma,
|
||||
bool clamp_top, bool clamp_bottom,
|
||||
const CopyFilterCoefficientArray& filter_coefficients)
|
||||
{
|
||||
auto* destination_texture = static_cast<OGLTexture*>(entry->texture.get());
|
||||
g_renderer->ResetAPIState(); // reset any game specific settings
|
||||
|
||||
// Make sure to resolve anything we need to read from.
|
||||
const GLuint read_texture = is_depth_copy ?
|
||||
FramebufferManager::ResolveAndGetDepthTarget(src_rect) :
|
||||
FramebufferManager::ResolveAndGetRenderTarget(src_rect);
|
||||
|
||||
FramebufferManager::SetFramebuffer(destination_texture->GetFramebuffer());
|
||||
|
||||
glActiveTexture(GL_TEXTURE9);
|
||||
glBindTexture(GL_TEXTURE_2D_ARRAY, read_texture);
|
||||
if (scale_by_half)
|
||||
g_sampler_cache->BindLinearSampler(9);
|
||||
else
|
||||
g_sampler_cache->BindNearestSampler(9);
|
||||
|
||||
glViewport(0, 0, destination_texture->GetConfig().width, destination_texture->GetConfig().height);
|
||||
|
||||
auto uid = TextureConversionShaderGen::GetShaderUid(dst_format, is_depth_copy, is_intensity,
|
||||
scale_by_half,
|
||||
NeedsCopyFilterInShader(filter_coefficients));
|
||||
|
||||
auto it = m_efb_copy_programs.emplace(uid, EFBCopyShader());
|
||||
EFBCopyShader& shader = it.first->second;
|
||||
bool created = it.second;
|
||||
|
||||
if (created)
|
||||
{
|
||||
ShaderCode code = TextureConversionShaderGen::GenerateShader(APIType::OpenGL, uid.GetUidData());
|
||||
|
||||
std::string geo_program = "";
|
||||
char prefix = 'f';
|
||||
if (g_ActiveConfig.stereo_mode != StereoMode::Off)
|
||||
{
|
||||
geo_program = GLSL_PROGRAM_GS;
|
||||
prefix = 'v';
|
||||
}
|
||||
|
||||
ProgramShaderCache::CompileShader(shader.shader,
|
||||
StringFromFormat(GLSL_PROGRAM_VS, prefix, prefix),
|
||||
code.GetBuffer(), geo_program);
|
||||
|
||||
shader.position_uniform = glGetUniformLocation(shader.shader.glprogid, "copy_position");
|
||||
shader.pixel_height_uniform = glGetUniformLocation(shader.shader.glprogid, "pixel_height");
|
||||
shader.gamma_rcp_uniform = glGetUniformLocation(shader.shader.glprogid, "gamma_rcp");
|
||||
shader.clamp_tb_uniform = glGetUniformLocation(shader.shader.glprogid, "clamp_tb");
|
||||
shader.filter_coefficients_uniform =
|
||||
glGetUniformLocation(shader.shader.glprogid, "filter_coefficients");
|
||||
}
|
||||
|
||||
shader.shader.Bind();
|
||||
|
||||
TargetRectangle R = g_renderer->ConvertEFBRectangle(src_rect);
|
||||
glUniform4f(shader.position_uniform, static_cast<float>(R.left), static_cast<float>(R.top),
|
||||
static_cast<float>(R.right), static_cast<float>(R.bottom));
|
||||
glUniform1f(shader.pixel_height_uniform, g_ActiveConfig.bCopyEFBScaled ?
|
||||
1.0f / g_renderer->GetTargetHeight() :
|
||||
1.0f / EFB_HEIGHT);
|
||||
glUniform1f(shader.gamma_rcp_uniform, 1.0f / gamma);
|
||||
glUniform2f(shader.clamp_tb_uniform,
|
||||
clamp_bottom ? (1.0f - src_rect.bottom / static_cast<float>(EFB_HEIGHT)) : 0.0f,
|
||||
clamp_top ? (1.0f - src_rect.top / static_cast<float>(EFB_HEIGHT)) : 1.0f);
|
||||
glUniform3f(shader.filter_coefficients_uniform, filter_coefficients[0], filter_coefficients[1],
|
||||
filter_coefficients[2]);
|
||||
|
||||
ProgramShaderCache::BindVertexFormat(nullptr);
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
|
||||
g_renderer->RestoreAPIState();
|
||||
}
|
||||
} // namespace OGL
|
@ -1,108 +0,0 @@
|
||||
// Copyright 2008 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <utility>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/GL/GLUtil.h"
|
||||
#include "VideoBackends/OGL/ProgramShaderCache.h"
|
||||
|
||||
#include "VideoCommon/TextureCacheBase.h"
|
||||
#include "VideoCommon/TextureConversionShader.h"
|
||||
#include "VideoCommon/TextureConverterShaderGen.h"
|
||||
#include "VideoCommon/VideoCommon.h"
|
||||
|
||||
class AbstractTexture;
|
||||
class StreamBuffer;
|
||||
struct TextureConfig;
|
||||
|
||||
namespace OGL
|
||||
{
|
||||
class TextureCache : public TextureCacheBase
|
||||
{
|
||||
public:
|
||||
TextureCache();
|
||||
~TextureCache();
|
||||
|
||||
static TextureCache* GetInstance();
|
||||
|
||||
bool SupportsGPUTextureDecode(TextureFormat format, TLUTFormat palette_format) override;
|
||||
void DecodeTextureOnGPU(TCacheEntry* entry, u32 dst_level, const u8* data, size_t data_size,
|
||||
TextureFormat format, u32 width, u32 height, u32 aligned_width,
|
||||
u32 aligned_height, u32 row_stride, const u8* palette,
|
||||
TLUTFormat palette_format) override;
|
||||
|
||||
const SHADER& GetColorCopyProgram() const;
|
||||
GLuint GetColorCopyPositionUniform() const;
|
||||
|
||||
private:
|
||||
struct PaletteShader
|
||||
{
|
||||
SHADER shader;
|
||||
GLuint buffer_offset_uniform;
|
||||
GLuint multiplier_uniform;
|
||||
GLuint copy_position_uniform;
|
||||
};
|
||||
|
||||
struct TextureDecodingProgramInfo
|
||||
{
|
||||
const TextureConversionShaderTiled::DecodingShaderInfo* base_info = nullptr;
|
||||
SHADER program;
|
||||
GLint uniform_dst_size = -1;
|
||||
GLint uniform_src_size = -1;
|
||||
GLint uniform_src_row_stride = -1;
|
||||
GLint uniform_src_offset = -1;
|
||||
GLint uniform_palette_offset = -1;
|
||||
bool valid = false;
|
||||
};
|
||||
|
||||
void ConvertTexture(TCacheEntry* destination, TCacheEntry* source, const void* palette,
|
||||
TLUTFormat format) override;
|
||||
|
||||
void CopyEFB(AbstractStagingTexture* dst, const EFBCopyParams& params, u32 native_width,
|
||||
u32 bytes_per_row, u32 num_blocks_y, u32 memory_stride, const EFBRectangle& src_rect,
|
||||
bool scale_by_half, float y_scale, float gamma, bool clamp_top, bool clamp_bottom,
|
||||
const CopyFilterCoefficientArray& filter_coefficients) override;
|
||||
|
||||
void CopyEFBToCacheEntry(TCacheEntry* entry, bool is_depth_copy, const EFBRectangle& src_rect,
|
||||
bool scale_by_half, EFBCopyFormat dst_format, bool is_intensity,
|
||||
float gamma, bool clamp_top, bool clamp_bottom,
|
||||
const CopyFilterCoefficientArray& filter_coefficients) override;
|
||||
|
||||
bool CompileShaders() override;
|
||||
void DeleteShaders() override;
|
||||
|
||||
bool CompilePaletteShader(TLUTFormat tlutfmt, const std::string& vcode, const std::string& pcode,
|
||||
const std::string& gcode);
|
||||
|
||||
void CreateTextureDecodingResources();
|
||||
void DestroyTextureDecodingResources();
|
||||
|
||||
struct EFBCopyShader
|
||||
{
|
||||
SHADER shader;
|
||||
GLuint position_uniform;
|
||||
GLuint pixel_height_uniform;
|
||||
GLuint gamma_rcp_uniform;
|
||||
GLuint clamp_tb_uniform;
|
||||
GLuint filter_coefficients_uniform;
|
||||
};
|
||||
|
||||
std::map<TextureConversionShaderGen::TCShaderUid, EFBCopyShader> m_efb_copy_programs;
|
||||
|
||||
SHADER m_colorCopyProgram;
|
||||
GLuint m_colorCopyPositionUniform;
|
||||
|
||||
std::array<PaletteShader, 3> m_palette_shaders;
|
||||
std::unique_ptr<StreamBuffer> m_palette_stream_buffer;
|
||||
GLuint m_palette_resolv_texture = 0;
|
||||
|
||||
std::map<std::pair<u32, u32>, TextureDecodingProgramInfo> m_texture_decoding_program_info;
|
||||
std::array<GLuint, TextureConversionShaderTiled::BUFFER_FORMAT_COUNT>
|
||||
m_texture_decoding_buffer_views;
|
||||
};
|
||||
}
|
@ -1,170 +0,0 @@
|
||||
// Copyright 2008 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
// Fast image conversion using OpenGL shaders.
|
||||
|
||||
#include "VideoBackends/OGL/TextureConverter.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/FileUtil.h"
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "Common/MsgHandler.h"
|
||||
#include "Common/StringUtil.h"
|
||||
|
||||
#include "Core/HW/Memmap.h"
|
||||
|
||||
#include "VideoBackends/OGL/FramebufferManager.h"
|
||||
#include "VideoBackends/OGL/OGLTexture.h"
|
||||
#include "VideoBackends/OGL/ProgramShaderCache.h"
|
||||
#include "VideoBackends/OGL/Render.h"
|
||||
#include "VideoBackends/OGL/SamplerCache.h"
|
||||
#include "VideoBackends/OGL/TextureCache.h"
|
||||
|
||||
#include "VideoCommon/ImageWrite.h"
|
||||
#include "VideoCommon/TextureConversionShader.h"
|
||||
#include "VideoCommon/VideoCommon.h"
|
||||
#include "VideoCommon/VideoConfig.h"
|
||||
|
||||
namespace OGL
|
||||
{
|
||||
namespace TextureConverter
|
||||
{
|
||||
namespace
|
||||
{
|
||||
struct EncodingProgram
|
||||
{
|
||||
SHADER program;
|
||||
GLint copy_position_uniform;
|
||||
GLint y_scale_uniform;
|
||||
GLint gamma_rcp_uniform;
|
||||
GLint clamp_tb_uniform;
|
||||
GLint filter_coefficients_uniform;
|
||||
};
|
||||
|
||||
std::map<EFBCopyParams, EncodingProgram> s_encoding_programs;
|
||||
std::unique_ptr<AbstractTexture> s_encoding_render_texture;
|
||||
|
||||
const int renderBufferWidth = EFB_WIDTH * 4;
|
||||
const int renderBufferHeight = 1024;
|
||||
} // namespace
|
||||
|
||||
static EncodingProgram& GetOrCreateEncodingShader(const EFBCopyParams& params)
|
||||
{
|
||||
auto iter = s_encoding_programs.find(params);
|
||||
if (iter != s_encoding_programs.end())
|
||||
return iter->second;
|
||||
|
||||
const char* shader =
|
||||
TextureConversionShaderTiled::GenerateEncodingShader(params, APIType::OpenGL);
|
||||
|
||||
#if defined(_DEBUG) || defined(DEBUGFAST)
|
||||
if (g_ActiveConfig.iLog & CONF_SAVESHADERS && shader)
|
||||
{
|
||||
static int counter = 0;
|
||||
std::string filename =
|
||||
StringFromFormat("%senc_%04i.txt", File::GetUserPath(D_DUMP_IDX).c_str(), counter++);
|
||||
|
||||
SaveData(filename, shader);
|
||||
}
|
||||
#endif
|
||||
|
||||
const char* VProgram = "void main()\n"
|
||||
"{\n"
|
||||
" vec2 rawpos = vec2(gl_VertexID&1, gl_VertexID&2);\n"
|
||||
" gl_Position = vec4(rawpos*2.0-1.0, 0.0, 1.0);\n"
|
||||
"}\n";
|
||||
|
||||
EncodingProgram program;
|
||||
if (!ProgramShaderCache::CompileShader(program.program, VProgram, shader))
|
||||
PanicAlert("Failed to compile texture encoding shader.");
|
||||
|
||||
program.copy_position_uniform = glGetUniformLocation(program.program.glprogid, "position");
|
||||
program.y_scale_uniform = glGetUniformLocation(program.program.glprogid, "y_scale");
|
||||
program.gamma_rcp_uniform = glGetUniformLocation(program.program.glprogid, "gamma_rcp");
|
||||
program.clamp_tb_uniform = glGetUniformLocation(program.program.glprogid, "clamp_tb");
|
||||
program.filter_coefficients_uniform =
|
||||
glGetUniformLocation(program.program.glprogid, "filter_coefficients");
|
||||
return s_encoding_programs.emplace(params, program).first->second;
|
||||
}
|
||||
|
||||
void Init()
|
||||
{
|
||||
s_encoding_render_texture = g_renderer->CreateTexture(TextureCache::GetEncodingTextureConfig());
|
||||
}
|
||||
|
||||
void Shutdown()
|
||||
{
|
||||
s_encoding_render_texture.reset();
|
||||
|
||||
for (auto& program : s_encoding_programs)
|
||||
program.second.program.Destroy();
|
||||
s_encoding_programs.clear();
|
||||
}
|
||||
|
||||
// dst_line_size, writeStride in bytes
|
||||
|
||||
static void EncodeToRamUsingShader(GLuint srcTexture, AbstractStagingTexture* destAddr,
|
||||
u32 dst_line_size, u32 dstHeight, u32 writeStride,
|
||||
bool linearFilter, float y_scale)
|
||||
{
|
||||
FramebufferManager::SetFramebuffer(
|
||||
static_cast<OGLTexture*>(s_encoding_render_texture.get())->GetFramebuffer());
|
||||
|
||||
// set source texture
|
||||
glActiveTexture(GL_TEXTURE9);
|
||||
glBindTexture(GL_TEXTURE_2D_ARRAY, srcTexture);
|
||||
|
||||
// We also linear filtering for both box filtering and downsampling higher resolutions to 1x
|
||||
// TODO: This only produces perfect downsampling for 2x IR, other resolutions will need more
|
||||
// complex down filtering to average all pixels and produce the correct result.
|
||||
// Also, box filtering won't be correct for anything other than 1x IR
|
||||
if (linearFilter || g_renderer->GetEFBScale() != 1 || y_scale > 1.0f)
|
||||
g_sampler_cache->BindLinearSampler(9);
|
||||
else
|
||||
g_sampler_cache->BindNearestSampler(9);
|
||||
|
||||
glViewport(0, 0, (GLsizei)(dst_line_size / 4), (GLsizei)dstHeight);
|
||||
|
||||
ProgramShaderCache::BindVertexFormat(nullptr);
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
|
||||
MathUtil::Rectangle<int> copy_rect(0, 0, dst_line_size / 4, dstHeight);
|
||||
|
||||
destAddr->CopyFromTexture(s_encoding_render_texture.get(), copy_rect, 0, 0, copy_rect);
|
||||
}
|
||||
|
||||
void EncodeToRamFromTexture(AbstractStagingTexture* dest, const EFBCopyParams& params,
|
||||
u32 native_width, u32 bytes_per_row, u32 num_blocks_y,
|
||||
u32 memory_stride, const EFBRectangle& src_rect, bool scale_by_half,
|
||||
float y_scale, float gamma, float clamp_top, float clamp_bottom,
|
||||
const TextureCacheBase::CopyFilterCoefficientArray& filter_coefficients)
|
||||
{
|
||||
g_renderer->ResetAPIState();
|
||||
|
||||
EncodingProgram& texconv_shader = GetOrCreateEncodingShader(params);
|
||||
|
||||
texconv_shader.program.Bind();
|
||||
glUniform4i(texconv_shader.copy_position_uniform, src_rect.left, src_rect.top, native_width,
|
||||
scale_by_half ? 2 : 1);
|
||||
glUniform1f(texconv_shader.y_scale_uniform, y_scale);
|
||||
glUniform1f(texconv_shader.gamma_rcp_uniform, 1.0f / gamma);
|
||||
glUniform2f(texconv_shader.clamp_tb_uniform, clamp_top, clamp_bottom);
|
||||
glUniform3f(texconv_shader.filter_coefficients_uniform, filter_coefficients[0],
|
||||
filter_coefficients[1], filter_coefficients[2]);
|
||||
|
||||
const GLuint read_texture = params.depth ?
|
||||
FramebufferManager::ResolveAndGetDepthTarget(src_rect) :
|
||||
FramebufferManager::ResolveAndGetRenderTarget(src_rect);
|
||||
|
||||
EncodeToRamUsingShader(read_texture, dest, bytes_per_row, num_blocks_y, memory_stride,
|
||||
scale_by_half && !params.depth, y_scale);
|
||||
|
||||
g_renderer->RestoreAPIState();
|
||||
}
|
||||
|
||||
} // namespace TextureConverter
|
||||
|
||||
} // namespace OGL
|
@ -1,33 +0,0 @@
|
||||
// Copyright 2008 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/GL/GLUtil.h"
|
||||
|
||||
#include "VideoCommon/TextureCacheBase.h"
|
||||
#include "VideoCommon/VideoCommon.h"
|
||||
|
||||
struct EFBCopyParams;
|
||||
class AbstractStagingTexture;
|
||||
|
||||
namespace OGL
|
||||
{
|
||||
// Converts textures between formats using shaders
|
||||
// TODO: support multiple texture formats
|
||||
namespace TextureConverter
|
||||
{
|
||||
void Init();
|
||||
void Shutdown();
|
||||
|
||||
// returns size of the encoded data (in bytes)
|
||||
void EncodeToRamFromTexture(
|
||||
AbstractStagingTexture* dest, const EFBCopyParams& params, u32 native_width, u32 bytes_per_row,
|
||||
u32 num_blocks_y, u32 memory_stride, const EFBRectangle& src_rect, bool scale_by_half,
|
||||
float y_scale, float gamma, float clamp_top, float clamp_bottom,
|
||||
const TextureCacheBase::CopyFilterCoefficientArray& filter_coefficients);
|
||||
}
|
||||
|
||||
} // namespace OGL
|
@ -9,17 +9,14 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "Common/Align.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/FileUtil.h"
|
||||
#include "Common/GL/GLExtensions/GLExtensions.h"
|
||||
#include "Common/StringUtil.h"
|
||||
|
||||
#include "VideoBackends/OGL/BoundingBox.h"
|
||||
#include "VideoBackends/OGL/OGLPipeline.h"
|
||||
#include "VideoBackends/OGL/ProgramShaderCache.h"
|
||||
#include "VideoBackends/OGL/Render.h"
|
||||
#include "VideoBackends/OGL/StreamBuffer.h"
|
||||
#include "VideoCommon/BoundingBox.h"
|
||||
|
||||
#include "VideoCommon/IndexGenerator.h"
|
||||
#include "VideoCommon/Statistics.h"
|
||||
@ -28,38 +25,127 @@
|
||||
|
||||
namespace OGL
|
||||
{
|
||||
// This are the initially requested size for the buffers expressed in bytes
|
||||
const u32 MAX_IBUFFER_SIZE = 2 * 1024 * 1024;
|
||||
const u32 MAX_VBUFFER_SIZE = 32 * 1024 * 1024;
|
||||
|
||||
VertexManager::VertexManager() : m_cpu_v_buffer(MAX_VBUFFER_SIZE), m_cpu_i_buffer(MAX_IBUFFER_SIZE)
|
||||
static void CheckBufferBinding()
|
||||
{
|
||||
CreateDeviceObjects();
|
||||
// The index buffer is part of the VAO state, therefore we need to bind it first.
|
||||
if (!ProgramShaderCache::IsValidVertexFormatBound())
|
||||
{
|
||||
ProgramShaderCache::BindVertexFormat(
|
||||
static_cast<GLVertexFormat*>(VertexLoaderManager::GetCurrentVertexFormat()));
|
||||
}
|
||||
}
|
||||
|
||||
VertexManager::VertexManager() = default;
|
||||
|
||||
VertexManager::~VertexManager()
|
||||
{
|
||||
DestroyDeviceObjects();
|
||||
}
|
||||
if (g_ActiveConfig.backend_info.bSupportsPaletteConversion)
|
||||
{
|
||||
glDeleteTextures(static_cast<GLsizei>(m_texel_buffer_views.size()),
|
||||
m_texel_buffer_views.data());
|
||||
}
|
||||
|
||||
void VertexManager::CreateDeviceObjects()
|
||||
{
|
||||
m_vertex_buffer = StreamBuffer::Create(GL_ARRAY_BUFFER, MAX_VBUFFER_SIZE);
|
||||
m_index_buffer = StreamBuffer::Create(GL_ELEMENT_ARRAY_BUFFER, MAX_IBUFFER_SIZE);
|
||||
}
|
||||
|
||||
void VertexManager::DestroyDeviceObjects()
|
||||
{
|
||||
m_vertex_buffer.reset();
|
||||
// VAO must be found when destroying the index buffer.
|
||||
CheckBufferBinding();
|
||||
m_texel_buffer.reset();
|
||||
m_index_buffer.reset();
|
||||
m_vertex_buffer.reset();
|
||||
}
|
||||
|
||||
bool VertexManager::Initialize()
|
||||
{
|
||||
if (!VertexManagerBase::Initialize())
|
||||
return false;
|
||||
|
||||
m_vertex_buffer = StreamBuffer::Create(GL_ARRAY_BUFFER, VERTEX_STREAM_BUFFER_SIZE);
|
||||
m_index_buffer = StreamBuffer::Create(GL_ELEMENT_ARRAY_BUFFER, INDEX_STREAM_BUFFER_SIZE);
|
||||
|
||||
if (g_ActiveConfig.backend_info.bSupportsPaletteConversion)
|
||||
{
|
||||
// The minimum MAX_TEXTURE_BUFFER_SIZE that the spec mandates is 65KB, we are asking for a 1MB
|
||||
// buffer here. This buffer is also used as storage for undecoded textures when compute shader
|
||||
// texture decoding is enabled, in which case the requested size is 32MB.
|
||||
GLint max_buffer_size;
|
||||
glGetIntegerv(GL_MAX_TEXTURE_BUFFER_SIZE, &max_buffer_size);
|
||||
m_texel_buffer = StreamBuffer::Create(
|
||||
GL_TEXTURE_BUFFER, std::min(max_buffer_size, static_cast<GLint>(TEXEL_STREAM_BUFFER_SIZE)));
|
||||
|
||||
// Allocate texture views backed by buffer.
|
||||
static constexpr std::array<std::pair<TexelBufferFormat, GLenum>, NUM_TEXEL_BUFFER_FORMATS>
|
||||
format_mapping = {{
|
||||
{TEXEL_BUFFER_FORMAT_R8_UINT, GL_R8UI},
|
||||
{TEXEL_BUFFER_FORMAT_R16_UINT, GL_R16UI},
|
||||
{TEXEL_BUFFER_FORMAT_RGBA8_UINT, GL_RGBA8},
|
||||
{TEXEL_BUFFER_FORMAT_R32G32_UINT, GL_RG32UI},
|
||||
}};
|
||||
glGenTextures(static_cast<GLsizei>(m_texel_buffer_views.size()), m_texel_buffer_views.data());
|
||||
glActiveTexture(GL_MUTABLE_TEXTURE_INDEX);
|
||||
for (const auto& it : format_mapping)
|
||||
{
|
||||
glBindTexture(GL_TEXTURE_BUFFER, m_texel_buffer_views[it.first]);
|
||||
glTexBuffer(GL_TEXTURE_BUFFER, it.second, m_texel_buffer->GetGLBufferId());
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void VertexManager::UploadUtilityUniforms(const void* uniforms, u32 uniforms_size)
|
||||
{
|
||||
ProgramShaderCache::InvalidateConstants();
|
||||
InvalidateConstants();
|
||||
ProgramShaderCache::UploadConstants(uniforms, uniforms_size);
|
||||
}
|
||||
|
||||
bool VertexManager::UploadTexelBuffer(const void* data, u32 data_size, TexelBufferFormat format,
|
||||
u32* out_offset)
|
||||
{
|
||||
if (data_size > m_texel_buffer->GetSize())
|
||||
return false;
|
||||
|
||||
const u32 elem_size = GetTexelBufferElementSize(format);
|
||||
const auto dst = m_texel_buffer->Map(data_size, elem_size);
|
||||
std::memcpy(dst.first, data, data_size);
|
||||
ADDSTAT(stats.thisFrame.bytesUniformStreamed, data_size);
|
||||
*out_offset = dst.second / elem_size;
|
||||
m_texel_buffer->Unmap(data_size);
|
||||
|
||||
// Bind the correct view to the texel buffer slot.
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_BUFFER, m_texel_buffer_views[static_cast<u32>(format)]);
|
||||
Renderer::GetInstance()->InvalidateTextureBinding(0);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VertexManager::UploadTexelBuffer(const void* data, u32 data_size, TexelBufferFormat format,
|
||||
u32* out_offset, const void* palette_data, u32 palette_size,
|
||||
TexelBufferFormat palette_format, u32* out_palette_offset)
|
||||
{
|
||||
const u32 elem_size = GetTexelBufferElementSize(format);
|
||||
const u32 palette_elem_size = GetTexelBufferElementSize(palette_format);
|
||||
const u32 reserve_size = data_size + palette_size + palette_elem_size;
|
||||
if (reserve_size > m_texel_buffer->GetSize())
|
||||
return false;
|
||||
|
||||
const auto dst = m_texel_buffer->Map(reserve_size, elem_size);
|
||||
const u32 palette_byte_offset = Common::AlignUp(data_size, palette_elem_size);
|
||||
std::memcpy(dst.first, data, data_size);
|
||||
std::memcpy(dst.first + palette_byte_offset, palette_data, palette_size);
|
||||
ADDSTAT(stats.thisFrame.bytesUniformStreamed, palette_byte_offset + palette_size);
|
||||
*out_offset = dst.second / elem_size;
|
||||
*out_palette_offset = (dst.second + palette_byte_offset) / palette_elem_size;
|
||||
m_texel_buffer->Unmap(palette_byte_offset + palette_size);
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_BUFFER, m_texel_buffer_views[static_cast<u32>(format)]);
|
||||
Renderer::GetInstance()->InvalidateTextureBinding(0);
|
||||
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
glBindTexture(GL_TEXTURE_BUFFER, m_texel_buffer_views[static_cast<u32>(palette_format)]);
|
||||
Renderer::GetInstance()->InvalidateTextureBinding(1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
GLuint VertexManager::GetVertexBufferHandle() const
|
||||
{
|
||||
return m_vertex_buffer->m_buffer;
|
||||
@ -70,37 +156,16 @@ GLuint VertexManager::GetIndexBufferHandle() const
|
||||
return m_index_buffer->m_buffer;
|
||||
}
|
||||
|
||||
static void CheckBufferBinding()
|
||||
void VertexManager::ResetBuffer(u32 vertex_stride)
|
||||
{
|
||||
// The index buffer is part of the VAO state, therefore we need to bind it first.
|
||||
if (!ProgramShaderCache::IsValidVertexFormatBound())
|
||||
{
|
||||
ProgramShaderCache::BindVertexFormat(
|
||||
static_cast<GLVertexFormat*>(VertexLoaderManager::GetCurrentVertexFormat()));
|
||||
}
|
||||
}
|
||||
CheckBufferBinding();
|
||||
|
||||
void VertexManager::ResetBuffer(u32 vertex_stride, bool cull_all)
|
||||
{
|
||||
if (cull_all)
|
||||
{
|
||||
// This buffer isn't getting sent to the GPU. Just allocate it on the cpu.
|
||||
m_cur_buffer_pointer = m_base_buffer_pointer = m_cpu_v_buffer.data();
|
||||
m_end_buffer_pointer = m_base_buffer_pointer + m_cpu_v_buffer.size();
|
||||
auto buffer = m_vertex_buffer->Map(MAXVBUFFERSIZE, vertex_stride);
|
||||
m_cur_buffer_pointer = m_base_buffer_pointer = buffer.first;
|
||||
m_end_buffer_pointer = buffer.first + MAXVBUFFERSIZE;
|
||||
|
||||
IndexGenerator::Start((u16*)m_cpu_i_buffer.data());
|
||||
}
|
||||
else
|
||||
{
|
||||
CheckBufferBinding();
|
||||
|
||||
auto buffer = m_vertex_buffer->Map(MAXVBUFFERSIZE, vertex_stride);
|
||||
m_cur_buffer_pointer = m_base_buffer_pointer = buffer.first;
|
||||
m_end_buffer_pointer = buffer.first + MAXVBUFFERSIZE;
|
||||
|
||||
buffer = m_index_buffer->Map(MAXIBUFFERSIZE * sizeof(u16));
|
||||
IndexGenerator::Start((u16*)buffer.first);
|
||||
}
|
||||
buffer = m_index_buffer->Map(MAXIBUFFERSIZE * sizeof(u16));
|
||||
IndexGenerator::Start((u16*)buffer.first);
|
||||
}
|
||||
|
||||
void VertexManager::CommitBuffer(u32 num_vertices, u32 vertex_stride, u32 num_indices,
|
||||
@ -120,31 +185,8 @@ void VertexManager::CommitBuffer(u32 num_vertices, u32 vertex_stride, u32 num_in
|
||||
ADDSTAT(stats.thisFrame.bytesIndexStreamed, index_data_size);
|
||||
}
|
||||
|
||||
void VertexManager::UploadConstants()
|
||||
void VertexManager::UploadUniforms()
|
||||
{
|
||||
ProgramShaderCache::UploadConstants();
|
||||
}
|
||||
|
||||
void VertexManager::DrawCurrentBatch(u32 base_index, u32 num_indices, u32 base_vertex)
|
||||
{
|
||||
if (::BoundingBox::active && !g_Config.BBoxUseFragmentShaderImplementation())
|
||||
{
|
||||
glEnable(GL_STENCIL_TEST);
|
||||
}
|
||||
|
||||
if (m_current_pipeline_object)
|
||||
{
|
||||
static_cast<Renderer*>(g_renderer.get())->SetPipeline(m_current_pipeline_object);
|
||||
static_cast<Renderer*>(g_renderer.get())->DrawIndexed(base_index, num_indices, base_vertex);
|
||||
}
|
||||
|
||||
if (::BoundingBox::active && !g_Config.BBoxUseFragmentShaderImplementation())
|
||||
{
|
||||
OGL::BoundingBox::StencilWasUpdated();
|
||||
glDisable(GL_STENCIL_TEST);
|
||||
}
|
||||
|
||||
g_Config.iSaveTargetId++;
|
||||
ClearEFBCache();
|
||||
}
|
||||
} // namespace OGL
|
||||
|
@ -4,8 +4,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/GL/GLUtil.h"
|
||||
@ -26,35 +26,34 @@ public:
|
||||
|
||||
// Handles the OpenGL details of drawing lots of vertices quickly.
|
||||
// Other functionality is moving out.
|
||||
class VertexManager : public VertexManagerBase
|
||||
class VertexManager final : public VertexManagerBase
|
||||
{
|
||||
public:
|
||||
VertexManager();
|
||||
~VertexManager();
|
||||
~VertexManager() override;
|
||||
|
||||
std::unique_ptr<NativeVertexFormat>
|
||||
CreateNativeVertexFormat(const PortableVertexDeclaration& vtx_decl) override;
|
||||
bool Initialize() override;
|
||||
|
||||
void UploadUtilityUniforms(const void* uniforms, u32 uniforms_size) override;
|
||||
bool UploadTexelBuffer(const void* data, u32 data_size, TexelBufferFormat format,
|
||||
u32* out_offset) override;
|
||||
bool UploadTexelBuffer(const void* data, u32 data_size, TexelBufferFormat format, u32* out_offset,
|
||||
const void* palette_data, u32 palette_size,
|
||||
TexelBufferFormat palette_format, u32* out_palette_offset) override;
|
||||
|
||||
GLuint GetVertexBufferHandle() const;
|
||||
GLuint GetIndexBufferHandle() const;
|
||||
|
||||
protected:
|
||||
void CreateDeviceObjects() override;
|
||||
void DestroyDeviceObjects() override;
|
||||
void ResetBuffer(u32 vertex_stride, bool cull_all) override;
|
||||
void ResetBuffer(u32 vertex_stride) override;
|
||||
void CommitBuffer(u32 num_vertices, u32 vertex_stride, u32 num_indices, u32* out_base_vertex,
|
||||
u32* out_base_index) override;
|
||||
void UploadConstants() override;
|
||||
void DrawCurrentBatch(u32 base_index, u32 num_indices, u32 base_vertex) override;
|
||||
void UploadUniforms() override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<StreamBuffer> m_vertex_buffer;
|
||||
std::unique_ptr<StreamBuffer> m_index_buffer;
|
||||
|
||||
// Alternative buffers in CPU memory for primatives we are going to discard.
|
||||
std::vector<u8> m_cpu_v_buffer;
|
||||
std::vector<u16> m_cpu_i_buffer;
|
||||
std::unique_ptr<StreamBuffer> m_texel_buffer;
|
||||
std::array<GLuint, NUM_TEXEL_BUFFER_FORMATS> m_texel_buffer_views{};
|
||||
};
|
||||
} // namespace OGL
|
||||
|
@ -50,13 +50,11 @@ Make AA apply instantly during gameplay if possible
|
||||
#include "VideoBackends/OGL/ProgramShaderCache.h"
|
||||
#include "VideoBackends/OGL/Render.h"
|
||||
#include "VideoBackends/OGL/SamplerCache.h"
|
||||
#include "VideoBackends/OGL/TextureCache.h"
|
||||
#include "VideoBackends/OGL/TextureConverter.h"
|
||||
#include "VideoBackends/OGL/VertexManager.h"
|
||||
#include "VideoBackends/OGL/VideoBackend.h"
|
||||
|
||||
#include "VideoCommon/OnScreenDisplay.h"
|
||||
#include "VideoCommon/VideoCommon.h"
|
||||
#include "VideoCommon/FramebufferManager.h"
|
||||
#include "VideoCommon/TextureCacheBase.h"
|
||||
#include "VideoCommon/VideoConfig.h"
|
||||
|
||||
namespace OGL
|
||||
@ -78,6 +76,7 @@ void VideoBackend::InitBackendInfo()
|
||||
{
|
||||
g_Config.backend_info.api_type = APIType::OpenGL;
|
||||
g_Config.backend_info.MaxTextureSize = 16384;
|
||||
g_Config.backend_info.bUsesLowerLeftOrigin = true;
|
||||
g_Config.backend_info.bSupportsExclusiveFullscreen = false;
|
||||
g_Config.backend_info.bSupportsOversizedViewports = true;
|
||||
g_Config.backend_info.bSupportsGeometryShaders = true;
|
||||
@ -89,6 +88,7 @@ void VideoBackend::InitBackendInfo()
|
||||
g_Config.backend_info.bSupportsLogicOp = true;
|
||||
g_Config.backend_info.bSupportsMultithreading = false;
|
||||
g_Config.backend_info.bSupportsCopyToVram = true;
|
||||
g_Config.backend_info.bSupportsLargePoints = true;
|
||||
|
||||
// TODO: There is a bug here, if texel buffers are not supported the graphics options
|
||||
// will show the option when it is not supported. The only way around this would be
|
||||
@ -173,17 +173,26 @@ bool VideoBackend::Initialize(const WindowSystemInfo& wsi)
|
||||
return false;
|
||||
|
||||
g_renderer = std::make_unique<Renderer>(std::move(main_gl_context), wsi.render_surface_scale);
|
||||
g_vertex_manager = std::make_unique<VertexManager>();
|
||||
g_perf_query = GetPerfQuery();
|
||||
ProgramShaderCache::Init();
|
||||
g_texture_cache = std::make_unique<TextureCache>();
|
||||
g_sampler_cache = std::make_unique<SamplerCache>();
|
||||
g_vertex_manager = std::make_unique<VertexManager>();
|
||||
g_shader_cache = std::make_unique<VideoCommon::ShaderCache>();
|
||||
if (!g_renderer->Initialize())
|
||||
g_framebuffer_manager = std::make_unique<FramebufferManager>();
|
||||
g_perf_query = GetPerfQuery();
|
||||
g_texture_cache = std::make_unique<TextureCacheBase>();
|
||||
g_sampler_cache = std::make_unique<SamplerCache>();
|
||||
BoundingBox::Init();
|
||||
|
||||
if (!g_vertex_manager->Initialize() || !g_shader_cache->Initialize() ||
|
||||
!g_renderer->Initialize() || !g_framebuffer_manager->Initialize() ||
|
||||
!g_texture_cache->Initialize())
|
||||
{
|
||||
PanicAlert("Failed to initialize renderer classes");
|
||||
Shutdown();
|
||||
return false;
|
||||
TextureConverter::Init();
|
||||
BoundingBox::Init(g_renderer->GetTargetWidth(), g_renderer->GetTargetHeight());
|
||||
return g_shader_cache->Initialize();
|
||||
}
|
||||
|
||||
g_shader_cache->InitializeShaderCache();
|
||||
return true;
|
||||
}
|
||||
|
||||
void VideoBackend::Shutdown()
|
||||
@ -191,13 +200,13 @@ void VideoBackend::Shutdown()
|
||||
g_shader_cache->Shutdown();
|
||||
g_renderer->Shutdown();
|
||||
BoundingBox::Shutdown();
|
||||
TextureConverter::Shutdown();
|
||||
g_shader_cache.reset();
|
||||
g_sampler_cache.reset();
|
||||
g_texture_cache.reset();
|
||||
ProgramShaderCache::Shutdown();
|
||||
g_perf_query.reset();
|
||||
g_vertex_manager.reset();
|
||||
g_framebuffer_manager.reset();
|
||||
g_shader_cache.reset();
|
||||
ProgramShaderCache::Shutdown();
|
||||
g_renderer.reset();
|
||||
ShutdownShared();
|
||||
}
|
||||
|
Reference in New Issue
Block a user