Move most backend functionality to VideoCommon

This commit is contained in:
Stenzek
2019-02-15 11:59:50 +10:00
parent 933f3ba008
commit f039149198
182 changed files with 8334 additions and 15917 deletions

View File

@ -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

View File

@ -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

View File

@ -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
)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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>

View File

@ -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">

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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 =

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;
};
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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();
}