Put Plugins/ in Core/, rename to VideoBackends

This commit is contained in:
Jasper St. Pierre
2013-09-10 23:12:54 -04:00
parent d6f0ecebb4
commit a7c7208103
145 changed files with 25 additions and 41 deletions

View File

@ -0,0 +1,488 @@
// Copyright 2013 Dolphin Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.
#include "Globals.h"
#include "FramebufferManager.h"
#include "VertexShaderGen.h"
#include "OnScreenDisplay.h"
#include "GLFunctions.h"
#include "TextureConverter.h"
#include "Render.h"
#include "HW/Memmap.h"
namespace OGL
{
int FramebufferManager::m_targetWidth;
int FramebufferManager::m_targetHeight;
int FramebufferManager::m_msaaSamples;
int FramebufferManager::m_msaaCoverageSamples;
GLuint FramebufferManager::m_efbFramebuffer;
GLuint FramebufferManager::m_efbColor; // Renderbuffer in MSAA mode; Texture otherwise
GLuint FramebufferManager::m_efbDepth; // Renderbuffer in MSAA mode; Texture otherwise
// Only used in MSAA mode.
GLuint FramebufferManager::m_resolvedFramebuffer;
GLuint FramebufferManager::m_resolvedColorTexture;
GLuint FramebufferManager::m_resolvedDepthTexture;
GLuint FramebufferManager::m_xfbFramebuffer;
// reinterpret pixel format
GLuint FramebufferManager::m_pixel_format_vao;
GLuint FramebufferManager::m_pixel_format_vbo;
SHADER FramebufferManager::m_pixel_format_shaders[2];
FramebufferManager::FramebufferManager(int targetWidth, int targetHeight, int msaaSamples, int msaaCoverageSamples)
{
m_efbFramebuffer = 0;
m_efbColor = 0;
m_efbDepth = 0;
m_resolvedFramebuffer = 0;
m_resolvedColorTexture = 0;
m_resolvedDepthTexture = 0;
m_xfbFramebuffer = 0;
m_targetWidth = targetWidth;
m_targetHeight = targetHeight;
m_msaaSamples = msaaSamples;
m_msaaCoverageSamples = msaaCoverageSamples;
// 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.
// Create EFB target.
glGenFramebuffers(1, &m_efbFramebuffer);
glActiveTexture(GL_TEXTURE0 + 9);
if (m_msaaSamples <= 1)
{
// EFB targets will be textures in non-MSAA mode.
GLuint glObj[3];
glGenTextures(3, glObj);
m_efbColor = glObj[0];
m_efbDepth = glObj[1];
m_resolvedColorTexture = glObj[2]; // needed for pixel format convertion
glBindTexture(getFbType(), m_efbColor);
glTexParameteri(getFbType(), GL_TEXTURE_MAX_LEVEL, 0);
glTexImage2D(getFbType(), 0, GL_RGBA8, m_targetWidth, m_targetHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glBindTexture(getFbType(), m_efbDepth);
glTexParameteri(getFbType(), GL_TEXTURE_MAX_LEVEL, 0);
glTexImage2D(getFbType(), 0, GL_DEPTH_COMPONENT24, m_targetWidth, m_targetHeight, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL);
glBindTexture(getFbType(), m_resolvedColorTexture);
glTexParameteri(getFbType(), GL_TEXTURE_MAX_LEVEL, 0);
glTexImage2D(getFbType(), 0, GL_RGBA8, m_targetWidth, m_targetHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
// Bind target textures to the EFB framebuffer.
glBindFramebuffer(GL_FRAMEBUFFER, m_efbFramebuffer);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, getFbType(), m_efbColor, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, getFbType(), m_efbDepth, 0);
GL_REPORT_FBO_ERROR();
}
else
{
// EFB targets will be renderbuffers in MSAA mode (required by OpenGL).
// Resolve targets will be created to transfer EFB to RAM textures.
// XFB framebuffer will be created to transfer EFB to XFB texture.
// Create EFB target renderbuffers.
GLuint glObj[2];
glGenRenderbuffers(2, glObj);
m_efbColor = glObj[0];
m_efbDepth = glObj[1];
glBindRenderbuffer(GL_RENDERBUFFER, m_efbColor);
if (m_msaaCoverageSamples)
glRenderbufferStorageMultisampleCoverageNV(GL_RENDERBUFFER, m_msaaCoverageSamples, m_msaaSamples, GL_RGBA8, m_targetWidth, m_targetHeight);
else
glRenderbufferStorageMultisample(GL_RENDERBUFFER, m_msaaSamples, GL_RGBA8, m_targetWidth, m_targetHeight);
glBindRenderbuffer(GL_RENDERBUFFER, m_efbDepth);
if (m_msaaCoverageSamples)
glRenderbufferStorageMultisampleCoverageNV(GL_RENDERBUFFER, m_msaaCoverageSamples, m_msaaSamples, GL_DEPTH_COMPONENT24, m_targetWidth, m_targetHeight);
else
glRenderbufferStorageMultisample(GL_RENDERBUFFER, m_msaaSamples, GL_DEPTH_COMPONENT24, m_targetWidth, m_targetHeight);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
// Bind target renderbuffers to EFB framebuffer.
glBindFramebuffer(GL_FRAMEBUFFER, m_efbFramebuffer);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_efbColor);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_efbDepth);
GL_REPORT_FBO_ERROR();
// Create resolved targets for transferring multisampled EFB to texture.
glGenFramebuffers(1, &m_resolvedFramebuffer);
glGenTextures(2, glObj);
m_resolvedColorTexture = glObj[0];
m_resolvedDepthTexture = glObj[1];
glBindTexture(getFbType(), m_resolvedColorTexture);
glTexParameteri(getFbType(), GL_TEXTURE_MAX_LEVEL, 0);
glTexImage2D(getFbType(), 0, GL_RGBA8, m_targetWidth, m_targetHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glBindTexture(getFbType(), m_resolvedDepthTexture);
glTexParameteri(getFbType(), GL_TEXTURE_MAX_LEVEL, 0);
glTexImage2D(getFbType(), 0, GL_DEPTH_COMPONENT24, m_targetWidth, m_targetHeight, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL);
// Bind resolved textures to resolved framebuffer.
glBindFramebuffer(GL_FRAMEBUFFER, m_resolvedFramebuffer);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, getFbType(), m_resolvedColorTexture, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, getFbType(), m_resolvedDepthTexture, 0);
GL_REPORT_FBO_ERROR();
// Return to EFB framebuffer.
glBindFramebuffer(GL_FRAMEBUFFER, m_efbFramebuffer);
}
// Create XFB framebuffer; targets will be created elsewhere.
glGenFramebuffers(1, &m_xfbFramebuffer);
// EFB framebuffer is currently bound, make sure to clear its alpha value to 1.f
glViewport(0, 0, m_targetWidth, m_targetHeight);
glScissor(0, 0, m_targetWidth, m_targetHeight);
glClearColor(0.f, 0.f, 0.f, 1.f);
glClearDepthf(1.0f);
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
// reinterpret pixel format
glGenBuffers(1, &m_pixel_format_vbo);
glGenVertexArrays(1, &m_pixel_format_vao);
glBindVertexArray(m_pixel_format_vao);
glBindBuffer(GL_ARRAY_BUFFER, m_pixel_format_vbo);
glEnableVertexAttribArray(SHADER_POSITION_ATTRIB);
glVertexAttribPointer(SHADER_POSITION_ATTRIB, 2, GL_FLOAT, 0, sizeof(GLfloat)*2, NULL);
float vertices[] = {
-1.0, -1.0,
1.0, -1.0,
-1.0, 1.0,
1.0, 1.0,
};
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
char vs[] =
"ATTRIN vec2 rawpos;\n"
"void main(void) {\n"
" gl_Position = vec4(rawpos,0,1);\n"
"}\n";
char ps_rgba6_to_rgb8[] =
"uniform sampler2DRect samp9;\n"
"COLOROUT(ocol0)\n"
"void main()\n"
"{\n"
" ivec4 src6 = ivec4(round(texture2DRect(samp9, gl_FragCoord.xy) * 63.f));\n"
" ivec4 dst8;\n"
" dst8.r = (src6.r << 2) | (src6.g >> 4);\n"
" dst8.g = ((src6.g & 0xF) << 4) | (src6.b >> 2);\n"
" dst8.b = ((src6.b & 0x3) << 6) | src6.a;\n"
" dst8.a = 255;\n"
" ocol0 = float4(dst8) / 255.f;\n"
"}";
char ps_rgb8_to_rgba6[] =
"uniform sampler2DRect samp9;\n"
"COLOROUT(ocol0)\n"
"void main()\n"
"{\n"
" ivec4 src8 = ivec4(round(texture2DRect(samp9, gl_FragCoord.xy) * 255.f));\n"
" ivec4 dst6;\n"
" dst6.r = src8.r >> 2;\n"
" dst6.g = ((src8.r & 0x3) << 4) | (src8.g >> 4);\n"
" dst6.b = ((src8.g & 0xF) << 2) | (src8.b >> 6);\n"
" dst6.a = src8.b & 0x3F;\n"
" ocol0 = float4(dst6) / 63.f;\n"
"}";
if(g_ogl_config.eSupportedGLSLVersion != GLSLES2)
{
// HACK: This shaders aren't glsles2 compatible as glsles2 don't support bit operations
// it could be workaround by floor + frac + tons off additions, but I think it isn't worth
ProgramShaderCache::CompileShader(m_pixel_format_shaders[0], vs, ps_rgb8_to_rgba6);
ProgramShaderCache::CompileShader(m_pixel_format_shaders[1], vs, ps_rgba6_to_rgb8);
}
}
FramebufferManager::~FramebufferManager()
{
glBindFramebuffer(GL_FRAMEBUFFER, 0);
GLuint glObj[3];
// Note: OpenGL deletion functions silently ignore parameters of "0".
glObj[0] = m_efbFramebuffer;
glObj[1] = m_resolvedFramebuffer;
glObj[2] = m_xfbFramebuffer;
glDeleteFramebuffers(3, glObj);
m_efbFramebuffer = 0;
m_xfbFramebuffer = 0;
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;
if (m_msaaSamples <= 1)
glDeleteTextures(2, glObj);
else
glDeleteRenderbuffers(2, glObj);
m_efbColor = 0;
m_efbDepth = 0;
// reinterpret pixel format
glDeleteVertexArrays(1, &m_pixel_format_vao);
glDeleteBuffers(1, &m_pixel_format_vbo);
m_pixel_format_shaders[0].Destroy();
m_pixel_format_shaders[1].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.ClampLL(0, 0, m_targetWidth, m_targetHeight);
// Resolve.
glBindFramebuffer(GL_READ_FRAMEBUFFER, m_efbFramebuffer);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_resolvedFramebuffer);
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);
return m_resolvedColorTexture;
}
}
GLuint FramebufferManager::GetEFBDepthTexture(const EFBRectangle& sourceRc)
{
if (m_msaaSamples <= 1)
{
return m_efbDepth;
}
else
{
// Transfer the EFB to a resolved texture. EXT_framebuffer_blit is
// required.
TargetRectangle targetRc = g_renderer->ConvertEFBRectangle(sourceRc);
targetRc.ClampLL(0, 0, m_targetWidth, m_targetHeight);
// Resolve.
glBindFramebuffer(GL_READ_FRAMEBUFFER, m_efbFramebuffer);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_resolvedFramebuffer);
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);
return m_resolvedDepthTexture;
}
}
void FramebufferManager::CopyToRealXFB(u32 xfbAddr, u32 fbWidth, u32 fbHeight, const EFBRectangle& sourceRc,float Gamma)
{
u8* xfb_in_ram = Memory::GetPointer(xfbAddr);
if (!xfb_in_ram)
{
WARN_LOG(VIDEO, "Tried to copy to invalid XFB address");
return;
}
TargetRectangle targetRc = g_renderer->ConvertEFBRectangle(sourceRc);
TextureConverter::EncodeToRamYUYV(ResolveAndGetRenderTarget(sourceRc), targetRc, xfb_in_ram, fbWidth, fbHeight);
}
void FramebufferManager::SetFramebuffer(GLuint fb)
{
glBindFramebuffer(GL_FRAMEBUFFER, fb != 0 ? fb : GetEFBFramebuffer());
}
// 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)
{
if(g_ogl_config.eSupportedGLSLVersion == GLSLES2) {
// This feature isn't supported by glsles2
// TODO: move this to InitBackendInfo
// We have to disable both the active and the stored config. Else we
// would either
// show this line per format change in one frame or
// once per frame.
OSD::AddMessage("Format Change Emulation isn't supported by your GPU.", 10000);
g_ActiveConfig.bEFBEmulateFormatChanges = false;
g_Config.bEFBEmulateFormatChanges = false;
return;
}
g_renderer->ResetAPIState();
GLuint src_texture = 0;
if(m_msaaSamples > 1)
{
// MSAA mode, so resolve first
glBindFramebuffer(GL_READ_FRAMEBUFFER, m_efbFramebuffer);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_resolvedFramebuffer);
glBlitFramebuffer(
0, 0, m_targetWidth, m_targetHeight,
0, 0, m_targetWidth, m_targetHeight,
GL_COLOR_BUFFER_BIT, GL_NEAREST
);
// Return to EFB.
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_efbFramebuffer);
src_texture = m_resolvedColorTexture;
}
else
{
// non-MSAA mode, so switch textures
src_texture = m_efbColor;
m_efbColor = m_resolvedColorTexture;
m_resolvedColorTexture = src_texture;
// also switch them on fbo
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, getFbType(), m_efbColor, 0);
}
glViewport(0,0, m_targetWidth, m_targetHeight);
glActiveTexture(GL_TEXTURE0 + 9);
glBindTexture(getFbType(), src_texture);
m_pixel_format_shaders[convtype ? 1 : 0].Bind();
glBindVertexArray(m_pixel_format_vao);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
g_renderer->RestoreAPIState();
}
XFBSource::~XFBSource()
{
glDeleteTextures(1, &texture);
}
void XFBSource::Draw(const MathUtil::Rectangle<float> &sourcerc,
const MathUtil::Rectangle<float> &drawrc, int width, int height) const
{
// Texture map xfbSource->texture onto the main buffer
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
glBlitFramebuffer(sourcerc.left, sourcerc.bottom, sourcerc.right, sourcerc.top,
drawrc.left, drawrc.bottom, drawrc.right, drawrc.top,
GL_COLOR_BUFFER_BIT, GL_LINEAR);
GL_REPORT_ERRORD();
}
void XFBSource::DecodeToTexture(u32 xfbAddr, u32 fbWidth, u32 fbHeight)
{
TextureConverter::DecodeToTexture(xfbAddr, fbWidth, fbHeight, texture);
}
void XFBSource::CopyEFB(float Gamma)
{
g_renderer->ResetAPIState();
// Copy EFB data to XFB and restore render target again
glBindFramebuffer(GL_READ_FRAMEBUFFER, FramebufferManager::GetEFBFramebuffer());
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, FramebufferManager::GetXFBFramebuffer());
// Bind texture.
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
GL_REPORT_FBO_ERROR();
glBlitFramebuffer(
0, 0, texWidth, texHeight,
0, 0, texWidth, texHeight,
GL_COLOR_BUFFER_BIT, GL_NEAREST
);
// Return to EFB.
FramebufferManager::SetFramebuffer(0);
g_renderer->RestoreAPIState();
}
XFBSourceBase* FramebufferManager::CreateXFBSource(unsigned int target_width, unsigned int target_height)
{
GLuint texture;
glGenTextures(1, &texture);
glActiveTexture(GL_TEXTURE0 + 9);
glBindTexture(GL_TEXTURE_2D, texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, target_width, target_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
return new XFBSource(texture);
}
void FramebufferManager::GetTargetSize(unsigned int *width, unsigned int *height, const EFBRectangle& sourceRc)
{
*width = m_targetWidth;
*height = m_targetHeight;
}
} // namespace OGL

View File

@ -0,0 +1,131 @@
// Copyright 2013 Dolphin Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.
#ifndef _FRAMEBUFFERMANAGER_H_
#define _FRAMEBUFFERMANAGER_H_
#include "GLUtil.h"
#include "FramebufferManagerBase.h"
#include "ProgramShaderCache.h"
#include "Render.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 {
struct XFBSource : public XFBSourceBase
{
XFBSource(GLuint tex) : texture(tex) {}
~XFBSource();
void CopyEFB(float Gamma);
void DecodeToTexture(u32 xfbAddr, u32 fbWidth, u32 fbHeight);
void Draw(const MathUtil::Rectangle<float> &sourcerc,
const MathUtil::Rectangle<float> &drawrc, int width, int height) const;
const GLuint texture;
};
inline GLenum getFbType()
{
#ifndef USE_GLES3
return GL_TEXTURE_RECTANGLE;
#endif
return GL_TEXTURE_2D;
}
class FramebufferManager : public FramebufferManagerBase
{
public:
FramebufferManager(int targetWidth, int targetHeight, int msaaSamples, int msaaCoverageSamples);
~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 GLuint GetEFBFramebuffer() { return m_efbFramebuffer; }
static GLuint GetXFBFramebuffer() { return m_xfbFramebuffer; }
// Resolved framebuffer is only used in MSAA mode.
static GLuint GetResolvedFramebuffer() { return m_resolvedFramebuffer; }
static void SetFramebuffer(GLuint fb);
// 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);
private:
XFBSourceBase* CreateXFBSource(unsigned int target_width, unsigned int target_height);
void GetTargetSize(unsigned int *width, unsigned int *height, const EFBRectangle& sourceRc);
void CopyToRealXFB(u32 xfbAddr, u32 fbWidth, u32 fbHeight, const EFBRectangle& sourceRc,float Gamma);
static int m_targetWidth;
static int m_targetHeight;
static int m_msaaSamples;
static int m_msaaCoverageSamples;
static GLuint m_efbFramebuffer;
static GLuint m_efbColor; // Renderbuffer in MSAA mode; Texture otherwise
static GLuint m_efbDepth; // Renderbuffer in MSAA mode; Texture otherwise
// Only used in MSAA mode and to convert pixel format
static GLuint m_resolvedFramebuffer; // will be hot swapped with m_efbColor on non-msaa pixel format change
static GLuint m_resolvedColorTexture;
static GLuint m_resolvedDepthTexture;
static GLuint m_xfbFramebuffer; // Only used in MSAA mode
// For pixel format draw
static GLuint m_pixel_format_vbo;
static GLuint m_pixel_format_vao;
static SHADER m_pixel_format_shaders[2];
};
} // namespace OGL
#endif

View File

@ -0,0 +1,138 @@
// Copyright 2013 Dolphin Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.
#include "DriverDetails.h"
#include "GLFunctions.h"
#include "Log.h"
#include <dlfcn.h>
#ifdef USE_GLES3
PFNGLMAPBUFFERRANGEPROC glMapBufferRange;
PFNGLUNMAPBUFFERPROC glUnmapBuffer;
PFNGLBINDBUFFERRANGEPROC glBindBufferRange;
PFNGLBLITFRAMEBUFFERPROC glBlitFramebuffer;
PFNGLGENVERTEXARRAYSPROC glGenVertexArrays;
PFNGLDELETEVERTEXARRAYSPROC glDeleteVertexArrays;
PFNGLBINDVERTEXARRAYPROC glBindVertexArray;
PFNGLCLIENTWAITSYNCPROC glClientWaitSync;
PFNGLDELETESYNCPROC glDeleteSync;
PFNGLFENCESYNCPROC glFenceSync;
PFNGLSAMPLERPARAMETERFPROC glSamplerParameterf;
PFNGLSAMPLERPARAMETERIPROC glSamplerParameteri;
PFNGLSAMPLERPARAMETERFVPROC glSamplerParameterfv;
PFNGLBINDSAMPLERPROC glBindSampler;
PFNGLDELETESAMPLERSPROC glDeleteSamplers;
PFNGLGENSAMPLERSPROC glGenSamplers;
PFNGLGETPROGRAMBINARYPROC glGetProgramBinary;
PFNGLPROGRAMBINARYPROC glProgramBinary;
PFNGLPROGRAMPARAMETERIPROC glProgramParameteri;
PFNGLDRAWRANGEELEMENTSPROC glDrawRangeElements;
PFNGLRENDERBUFFERSTORAGEMULTISAMPLE glRenderbufferStorageMultisample;
PFNGLGETUNIFORMBLOCKINDEXPROC glGetUniformBlockIndex;
PFNGLUNIFORMBLOCKBINDINGPROC glUniformBlockBinding;
PFNGLBEGINQUERYPROC glBeginQuery;
PFNGLENDQUERYPROC glEndQuery;
PFNGLGETQUERYOBJECTUIVPROC glGetQueryObjectuiv;
PFNGLDELETEQUERIESPROC glDeleteQueries;
PFNGLGENQUERIESPROC glGenQueries;
#endif
namespace GLFunc
{
void *self;
void LoadFunction(const char *name, void **func)
{
#ifdef USE_GLES3
*func = (void*)eglGetProcAddress(name);
if (*func == NULL)
{
// Fall back to trying dlsym
if (self) // Just in case dlopen fails
*func = dlsym(self, name);
if (*func == NULL)
{
ERROR_LOG(VIDEO, "Couldn't load function %s", name);
exit(0);
}
}
#endif
}
void Init()
{
self = dlopen(NULL, RTLD_LAZY);
LoadFunction("glUnmapBuffer", (void**)&glUnmapBuffer);
if (DriverDetails::HasBug(DriverDetails::BUG_ISTEGRA))
{
LoadFunction("glBeginQueryEXT", (void**)&glBeginQuery);
LoadFunction("glEndQueryEXT", (void**)&glEndQuery);
LoadFunction("glGetQueryObjectuivEXT", (void**)&glGetQueryObjectuiv);
LoadFunction("glDeleteQueriesEXT", (void**)&glDeleteQueries);
LoadFunction("glGenQueriesEXT", (void**)&glGenQueries);
LoadFunction("glMapBufferRangeNV", (void**)&glMapBufferRange);
LoadFunction("glBindBufferRangeNV", (void**)&glBindBufferRange);
LoadFunction("glBlitFramebufferNV", (void**)&glBlitFramebuffer);
LoadFunction("glGenVertexArraysOES", (void**)&glGenVertexArrays);
LoadFunction("glDeleteVertexArraysOES", (void**)&glDeleteVertexArrays);
LoadFunction("glBindVertexArrayOES", (void**)&glBindVertexArray);
LoadFunction("glRenderbufferStorageMultisampleNV", (void**)&glRenderbufferStorageMultisample);
LoadFunction("glGetUniformBlockIndexNV", (void**)&glGetUniformBlockIndex);
LoadFunction("glUniformBlockBindingNV", (void**)&glUniformBlockBinding);
}
else
{
LoadFunction("glBeginQuery", (void**)&glBeginQuery);
LoadFunction("glEndQuery", (void**)&glEndQuery);
LoadFunction("glGetQueryObjectuiv", (void**)&glGetQueryObjectuiv);
LoadFunction("glDeleteQueries", (void**)&glDeleteQueries);
LoadFunction("glGenQueries", (void**)&glGenQueries);
LoadFunction("glMapBufferRange", (void**)&glMapBufferRange);
LoadFunction("glBindBufferRange", (void**)&glBindBufferRange);
LoadFunction("glBlitFramebuffer", (void**)&glBlitFramebuffer);
LoadFunction("glGenVertexArrays", (void**)&glGenVertexArrays);
LoadFunction("glDeleteVertexArrays", (void**)&glDeleteVertexArrays);
LoadFunction("glBindVertexArray", (void**)&glBindVertexArray);
LoadFunction("glClientWaitSync", (void**)&glClientWaitSync);
LoadFunction("glDeleteSync", (void**)&glDeleteSync);
LoadFunction("glFenceSync", (void**)&glFenceSync);
LoadFunction("glSamplerParameterf", (void**)&glSamplerParameterf);
LoadFunction("glSamplerParameteri", (void**)&glSamplerParameteri);
LoadFunction("glSamplerParameterfv", (void**)&glSamplerParameterfv);
LoadFunction("glBindSampler", (void**)&glBindSampler);
LoadFunction("glDeleteSamplers", (void**)&glDeleteSamplers);
LoadFunction("glGenSamplers", (void**)&glGenSamplers);
LoadFunction("glGetProgramBinary", (void**)&glGetProgramBinary);
LoadFunction("glProgramBinary", (void**)&glProgramBinary);
LoadFunction("glProgramParameteri", (void**)&glProgramParameteri);
LoadFunction("glDrawRangeElements", (void**)&glDrawRangeElements);
LoadFunction("glRenderbufferStorageMultisample", (void**)&glRenderbufferStorageMultisample);
LoadFunction("glGetUniformBlockIndex", (void**)&glGetUniformBlockIndex);
LoadFunction("glUniformBlockBinding", (void**)&glUniformBlockBinding);
}
dlclose(self);
}
}

View File

@ -0,0 +1,99 @@
// Copyright 2013 Dolphin Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.
#ifndef GLFUNCTIONS_H_
#define GLFUNCTIONS_H_
#include "GLInterface.h"
#ifdef USE_GLES3
typedef GLvoid* (*PFNGLMAPBUFFERPROC) (GLenum target, GLenum access);
typedef GLvoid* (*PFNGLMAPBUFFERRANGEPROC) (GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access);
typedef void (*PFNGLBINDBUFFERRANGEPROC) (GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size);
typedef GLboolean (*PFNGLUNMAPBUFFERPROC) (GLenum target);
typedef void (*PFNGLBLITFRAMEBUFFERPROC) (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter);
// VAOS
typedef void (*PFNGLGENVERTEXARRAYSPROC) (GLsizei n, GLuint* arrays);
typedef void (*PFNGLDELETEVERTEXARRAYSPROC) (GLsizei n, const GLuint* arrays);
typedef void (*PFNGLBINDVERTEXARRAYPROC) (GLuint array);
// Sync
typedef GLenum (*PFNGLCLIENTWAITSYNCPROC) (GLsync GLsync,GLbitfield flags,GLuint64 timeout);
typedef void (*PFNGLDELETESYNCPROC) (GLsync GLsync);
typedef GLsync (*PFNGLFENCESYNCPROC) (GLenum condition,GLbitfield flags);
//Sampler
typedef void (*PFNGLSAMPLERPARAMETERFPROC) (GLuint sampler, GLenum pname, GLfloat param);
typedef void (*PFNGLSAMPLERPARAMETERIPROC) (GLuint sampler, GLenum pname, GLint param);
typedef void (*PFNGLSAMPLERPARAMETERFVPROC) (GLuint sampler, GLenum pname, const GLfloat* params);
typedef void (*PFNGLBINDSAMPLERPROC) (GLuint unit, GLuint sampler);
typedef void (*PFNGLDELETESAMPLERSPROC) (GLsizei count, const GLuint * samplers);
typedef void (*PFNGLGENSAMPLERSPROC) (GLsizei count, GLuint* samplers);
//Program binary
typedef void (*PFNGLGETPROGRAMBINARYPROC) (GLuint program, GLsizei bufSize, GLsizei* length, GLenum *binaryFormat, GLvoid*binary);
typedef void (*PFNGLPROGRAMBINARYPROC) (GLuint program, GLenum binaryFormat, const void* binary, GLsizei length);
typedef void (*PFNGLPROGRAMPARAMETERIPROC) (GLuint program, GLenum pname, GLint value);
typedef GLuint (*PFNGLGETUNIFORMBLOCKINDEXPROC) (GLuint program, const GLchar* uniformBlockName);
typedef void (*PFNGLUNIFORMBLOCKBINDINGPROC) (GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding);
//Query
typedef void (*PFNGLBEGINQUERYPROC) (GLenum target, GLuint id);
typedef void (*PFNGLENDQUERYPROC) (GLenum target);
typedef void (*PFNGLGETQUERYOBJECTUIVPROC) (GLuint id, GLenum pname, GLuint* params);
typedef void (*PFNGLDELETEQUERIESPROC) (GLsizei n, const GLuint* ids);
typedef void (*PFNGLGENQUERIESPROC) (GLsizei n, GLuint* ids);
// glDraw*
typedef void (*PFNGLDRAWRANGEELEMENTSPROC) (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const GLvoid* indices);
// Multisample buffer
typedef void (*PFNGLRENDERBUFFERSTORAGEMULTISAMPLE) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height);
// ptrs
extern PFNGLBEGINQUERYPROC glBeginQuery;
extern PFNGLENDQUERYPROC glEndQuery;
extern PFNGLGETQUERYOBJECTUIVPROC glGetQueryObjectuiv;
extern PFNGLDELETEQUERIESPROC glDeleteQueries;
extern PFNGLGENQUERIESPROC glGenQueries;
extern PFNGLUNMAPBUFFERPROC glUnmapBuffer;
extern PFNGLMAPBUFFERRANGEPROC glMapBufferRange;
extern PFNGLBINDBUFFERRANGEPROC glBindBufferRange;
extern PFNGLBLITFRAMEBUFFERPROC glBlitFramebuffer;
extern PFNGLGENVERTEXARRAYSPROC glGenVertexArrays;
extern PFNGLDELETEVERTEXARRAYSPROC glDeleteVertexArrays;
extern PFNGLBINDVERTEXARRAYPROC glBindVertexArray;
extern PFNGLCLIENTWAITSYNCPROC glClientWaitSync;
extern PFNGLDELETESYNCPROC glDeleteSync;
extern PFNGLFENCESYNCPROC glFenceSync;
extern PFNGLGETPROGRAMBINARYPROC glGetProgramBinary;
extern PFNGLPROGRAMBINARYPROC glProgramBinary;
extern PFNGLPROGRAMPARAMETERIPROC glProgramParameteri;
extern PFNGLDRAWRANGEELEMENTSPROC glDrawRangeElements;
extern PFNGLRENDERBUFFERSTORAGEMULTISAMPLE glRenderbufferStorageMultisample;
//Sampler
extern PFNGLSAMPLERPARAMETERFPROC glSamplerParameterf;
extern PFNGLSAMPLERPARAMETERIPROC glSamplerParameteri;
extern PFNGLSAMPLERPARAMETERFVPROC glSamplerParameterfv;
extern PFNGLBINDSAMPLERPROC glBindSampler;
extern PFNGLDELETESAMPLERSPROC glDeleteSamplers;
extern PFNGLGENSAMPLERSPROC glGenSamplers;
extern PFNGLGETUNIFORMBLOCKINDEXPROC glGetUniformBlockIndex;
extern PFNGLUNIFORMBLOCKBINDINGPROC glUniformBlockBinding;
#endif
namespace GLFunc
{
void Init();
}
#endif

View File

@ -0,0 +1,174 @@
// Copyright 2013 Dolphin Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.
#include "Globals.h"
#include "VideoConfig.h"
#include "IniFile.h"
#include "Core.h"
#include "Host.h"
#include "VideoBackend.h"
#include "ConfigManager.h"
#include "Render.h"
#include "VertexShaderManager.h"
#include "GLUtil.h"
GLWindow GLWin;
cInterfaceBase *GLInterface;
namespace OGL
{
// Draw messages on top of the screen
unsigned int VideoBackend::PeekMessages()
{
return GLInterface->PeekMessages();
}
// Show the current FPS
void VideoBackend::UpdateFPSDisplay(const char *text)
{
char temp[100];
snprintf(temp, sizeof temp, "%s | OpenGL | %s", scm_rev_str, text);
return GLInterface->UpdateFPSDisplay(temp);
}
}
void InitInterface()
{
#if defined(USE_EGL) && USE_EGL
GLInterface = new cInterfaceEGL;
#elif defined(__APPLE__)
GLInterface = new cInterfaceAGL;
#elif defined(_WIN32)
GLInterface = new cInterfaceWGL;
#elif defined(HAVE_X11) && HAVE_X11
GLInterface = new cInterfaceGLX;
#endif
}
GLuint OpenGL_CompileProgram ( const char* vertexShader, const char* fragmentShader )
{
// generate objects
GLuint vertexShaderID = glCreateShader(GL_VERTEX_SHADER);
GLuint fragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER);
GLuint programID = glCreateProgram();
// compile vertex shader
glShaderSource(vertexShaderID, 1, &vertexShader, NULL);
glCompileShader(vertexShaderID);
#if defined(_DEBUG) || defined(DEBUGFAST) || defined(DEBUG_GLSL)
GLint Result = GL_FALSE;
char stringBuffer[1024];
GLsizei stringBufferUsage = 0;
glGetShaderiv(vertexShaderID, GL_COMPILE_STATUS, &Result);
glGetShaderInfoLog(vertexShaderID, 1024, &stringBufferUsage, stringBuffer);
if(Result && stringBufferUsage) {
ERROR_LOG(VIDEO, "GLSL vertex shader warnings:\n%s%s", stringBuffer, vertexShader);
} else if(!Result) {
ERROR_LOG(VIDEO, "GLSL vertex shader error:\n%s%s", stringBuffer, vertexShader);
} else {
DEBUG_LOG(VIDEO, "GLSL vertex shader compiled:\n%s", vertexShader);
}
bool shader_errors = !Result;
#endif
// compile fragment shader
glShaderSource(fragmentShaderID, 1, &fragmentShader, NULL);
glCompileShader(fragmentShaderID);
#if defined(_DEBUG) || defined(DEBUGFAST) || defined(DEBUG_GLSL)
glGetShaderiv(fragmentShaderID, GL_COMPILE_STATUS, &Result);
glGetShaderInfoLog(fragmentShaderID, 1024, &stringBufferUsage, stringBuffer);
if(Result && stringBufferUsage) {
ERROR_LOG(VIDEO, "GLSL fragment shader warnings:\n%s%s", stringBuffer, fragmentShader);
} else if(!Result) {
ERROR_LOG(VIDEO, "GLSL fragment shader error:\n%s%s", stringBuffer, fragmentShader);
} else {
DEBUG_LOG(VIDEO, "GLSL fragment shader compiled:\n%s", fragmentShader);
}
shader_errors |= !Result;
#endif
// link them
glAttachShader(programID, vertexShaderID);
glAttachShader(programID, fragmentShaderID);
glLinkProgram(programID);
#if defined(_DEBUG) || defined(DEBUGFAST) || defined(DEBUG_GLSL)
glGetProgramiv(programID, GL_LINK_STATUS, &Result);
glGetProgramInfoLog(programID, 1024, &stringBufferUsage, stringBuffer);
if(Result && stringBufferUsage) {
ERROR_LOG(VIDEO, "GLSL linker warnings:\n%s%s%s", stringBuffer, vertexShader, fragmentShader);
} else if(!Result && !shader_errors) {
ERROR_LOG(VIDEO, "GLSL linker error:\n%s%s%s", stringBuffer, vertexShader, fragmentShader);
}
#endif
// cleanup
glDeleteShader(vertexShaderID);
glDeleteShader(fragmentShaderID);
return programID;
}
GLuint OpenGL_ReportGLError(const char *function, const char *file, int line)
{
GLint err = glGetError();
if (err != GL_NO_ERROR)
{
ERROR_LOG(VIDEO, "%s:%d: (%s) OpenGL error 0x%x\n",
file, line, function, err);
}
return err;
}
void OpenGL_ReportARBProgramError()
{
#ifndef USE_GLES
const GLubyte* pstr = glGetString(GL_PROGRAM_ERROR_STRING_ARB);
if (pstr != NULL && pstr[0] != 0)
{
GLint loc = 0;
glGetIntegerv(GL_PROGRAM_ERROR_POSITION_ARB, &loc);
ERROR_LOG(VIDEO, "Program error at %d: ", loc);
ERROR_LOG(VIDEO, "%s", (char*)pstr);
ERROR_LOG(VIDEO, "\n");
}
#endif
}
bool OpenGL_ReportFBOError(const char *function, const char *file, int line)
{
unsigned int fbo_status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (fbo_status != GL_FRAMEBUFFER_COMPLETE)
{
const char *error = "unknown error";
switch (fbo_status)
{
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
error = "INCOMPLETE_ATTACHMENT";
break;
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
error = "INCOMPLETE_MISSING_ATTACHMENT";
break;
#ifndef USE_GLES
case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER:
error = "INCOMPLETE_DRAW_BUFFER";
break;
case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER:
error = "INCOMPLETE_READ_BUFFER";
break;
#endif
case GL_FRAMEBUFFER_UNSUPPORTED:
error = "UNSUPPORTED";
break;
}
ERROR_LOG(VIDEO, "%s:%d: (%s) OpenGL FBO error - %s\n",
file, line, function, error);
return false;
}
return true;
}

View File

@ -0,0 +1,84 @@
// Copyright 2013 Dolphin Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.
#ifndef _GLINIT_H_
#define _GLINIT_H_
#include "VideoConfig.h"
#include "MathUtil.h"
#include "GLInterface.h"
#ifndef GL_DEPTH24_STENCIL8_EXT // allows FBOs to support stencils
#define GL_DEPTH_STENCIL_EXT 0x84F9
#define GL_UNSIGNED_INT_24_8_EXT 0x84FA
#define GL_DEPTH24_STENCIL8_EXT 0x88F0
#define GL_TEXTURE_STENCIL_SIZE_EXT 0x88F1
#endif
#ifdef USE_GLES
#define TEX2D GL_TEXTURE_2D
#define PREC "highp"
#define TEXTYPE "sampler2D"
#define TEXFUNC "texture2D"
#ifdef USE_GLES3
#include "GLFunctions.h"
#define GLAPIENTRY GL_APIENTRY
#define GL_SAMPLES_PASSED GL_ANY_SAMPLES_PASSED
#define GL_READ_ONLY 0x88B8
#define GL_WRITE_ONLY 0x88B9
#define GL_READ_WRITE 0x88BA
#define GL_SRC1_ALPHA 0
#define GL_BGRA GL_RGBA
#define glDrawElementsBaseVertex(...)
#define glDrawRangeElementsBaseVertex(...)
#define glRenderbufferStorageMultisampleCoverageNV(...)
#endif
#else
#define TEX2D GL_TEXTURE_RECTANGLE_ARB
#define PREC
#define TEXTYPE "sampler2DRect"
#define TEXFUNC "texture2DRect"
#endif
#ifndef _WIN32
#include <sys/types.h>
#endif
void InitInterface();
// Helpers
GLuint OpenGL_CompileProgram(const char *vertexShader, const char *fragmentShader);
// Error reporting - use the convenient macros.
void OpenGL_ReportARBProgramError();
GLuint OpenGL_ReportGLError(const char *function, const char *file, int line);
bool OpenGL_ReportFBOError(const char *function, const char *file, int line);
#if defined(_DEBUG) || defined(DEBUGFAST)
#define GL_REPORT_ERROR() OpenGL_ReportGLError(__FUNCTION__, __FILE__, __LINE__)
#define GL_REPORT_ERRORD() OpenGL_ReportGLError(__FUNCTION__, __FILE__, __LINE__)
#define GL_REPORT_FBO_ERROR() OpenGL_ReportFBOError(__FUNCTION__, __FILE__, __LINE__)
#define GL_REPORT_PROGRAM_ERROR() OpenGL_ReportARBProgramError()
#else
__forceinline GLenum GL_REPORT_ERROR() { return GL_NO_ERROR; }
#define GL_REPORT_ERRORD() (void)GL_NO_ERROR
#define GL_REPORT_FBO_ERROR() (void)true
#define GL_REPORT_PROGRAM_ERROR() (void)0
#endif
// this should be removed in future, but as long as glsl is unstable, we should really read this messages
#if defined(_DEBUG) || defined(DEBUGFAST)
#define DEBUG_GLSL 1
#else
#define DEBUG_GLSL 0
#endif
// Isn't defined if we aren't using GLEW 1.6
#ifndef GL_ONE_MINUS_SRC1_ALPHA
#define GL_ONE_MINUS_SRC1_ALPHA 0x88FB
#endif
#endif // _GLINIT_H_

View File

@ -0,0 +1,13 @@
// Copyright 2013 Dolphin Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.
#ifndef _GLOBALS_H_
#define _GLOBALS_H_
#include "Common.h"
#include "VideoConfig.h"
#include "VideoCommon.h"
#endif // _GLOBALS_H_

View File

@ -0,0 +1,99 @@
// Copyright 2013 Dolphin Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.
#include "GLUtil.h"
#include "x64Emitter.h"
#include "x64ABI.h"
#include "MemoryUtil.h"
#include "ProgramShaderCache.h"
#include "VertexShaderGen.h"
#include "CPMemory.h"
#include "NativeVertexFormat.h"
#include "VertexManager.h"
// Here's some global state. We only use this to keep track of what we've sent to the OpenGL state
// machine.
namespace OGL
{
NativeVertexFormat* VertexManager::CreateNativeVertexFormat()
{
return new GLVertexFormat();
}
GLVertexFormat::GLVertexFormat()
{
}
GLVertexFormat::~GLVertexFormat()
{
glDeleteVertexArrays(1, &VAO);
}
inline GLuint VarToGL(VarType t)
{
static const GLuint lookup[5] = {
GL_BYTE, GL_UNSIGNED_BYTE, GL_SHORT, GL_UNSIGNED_SHORT, GL_FLOAT
};
return lookup[t];
}
void GLVertexFormat::Initialize(const PortableVertexDeclaration &_vtx_decl)
{
this->vtx_decl = _vtx_decl;
vertex_stride = vtx_decl.stride;
// We will not allow vertex components causing uneven strides.
if (vertex_stride & 3)
PanicAlert("Uneven vertex stride: %i", vertex_stride);
VertexManager *vm = (OGL::VertexManager*)g_vertex_manager;
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
// the element buffer is bound directly to the vao, so we must it set for every vao
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vm->m_index_buffers);
glBindBuffer(GL_ARRAY_BUFFER, vm->m_vertex_buffers);
glEnableVertexAttribArray(SHADER_POSITION_ATTRIB);
glVertexAttribPointer(SHADER_POSITION_ATTRIB, 3, GL_FLOAT, GL_FALSE, vtx_decl.stride, (u8*)NULL);
for (int i = 0; i < 3; i++) {
if (vtx_decl.num_normals > i) {
glEnableVertexAttribArray(SHADER_NORM0_ATTRIB+i);
glVertexAttribPointer(SHADER_NORM0_ATTRIB+i, vtx_decl.normal_gl_size, VarToGL(vtx_decl.normal_gl_type), GL_TRUE, vtx_decl.stride, (u8*)NULL + vtx_decl.normal_offset[i]);
}
}
for (int i = 0; i < 2; i++) {
if (vtx_decl.color_offset[i] != -1) {
glEnableVertexAttribArray(SHADER_COLOR0_ATTRIB+i);
glVertexAttribPointer(SHADER_COLOR0_ATTRIB+i, 4, GL_UNSIGNED_BYTE, GL_TRUE, vtx_decl.stride, (u8*)NULL + vtx_decl.color_offset[i]);
}
}
for (int i = 0; i < 8; i++) {
if (vtx_decl.texcoord_offset[i] != -1) {
glEnableVertexAttribArray(SHADER_TEXTURE0_ATTRIB+i);
glVertexAttribPointer(SHADER_TEXTURE0_ATTRIB+i, vtx_decl.texcoord_size[i], VarToGL(vtx_decl.texcoord_gl_type[i]),
GL_FALSE, vtx_decl.stride, (u8*)NULL + vtx_decl.texcoord_offset[i]);
}
}
if (vtx_decl.posmtx_offset != -1) {
glEnableVertexAttribArray(SHADER_POSMTX_ATTRIB);
glVertexAttribPointer(SHADER_POSMTX_ATTRIB, 4, GL_UNSIGNED_BYTE, GL_FALSE, vtx_decl.stride, (u8*)NULL + vtx_decl.posmtx_offset);
}
vm->m_last_vao = VAO;
}
void GLVertexFormat::SetupVertexPointers() {
}
}

View File

@ -0,0 +1,154 @@
#include "RenderBase.h"
#include "GLUtil.h"
#include "PerfQuery.h"
namespace OGL
{
PerfQuery::PerfQuery()
: m_query_read_pos()
, m_query_count()
{
for (u32 i = 0; i != ArraySize(m_query_buffer); ++i)
glGenQueries(1, &m_query_buffer[i].query_id);
ResetQuery();
}
PerfQuery::~PerfQuery()
{
for (u32 i = 0; i != ArraySize(m_query_buffer); ++i)
glDeleteQueries(1, &m_query_buffer[i].query_id);
}
void PerfQuery::EnableQuery(PerfQueryGroup type)
{
if (!ShouldEmulate())
return;
// Is this sane?
if (m_query_count > ArraySize(m_query_buffer) / 2)
WeakFlush();
if (ArraySize(m_query_buffer) == m_query_count)
{
FlushOne();
//ERROR_LOG(VIDEO, "Flushed query buffer early!");
}
// start query
if (type == PQG_ZCOMP_ZCOMPLOC || type == PQG_ZCOMP)
{
auto& entry = m_query_buffer[(m_query_read_pos + m_query_count) % ArraySize(m_query_buffer)];
glBeginQuery(GL_SAMPLES_PASSED, entry.query_id);
entry.query_type = type;
++m_query_count;
}
}
void PerfQuery::DisableQuery(PerfQueryGroup type)
{
if (!ShouldEmulate())
return;
// stop query
if (type == PQG_ZCOMP_ZCOMPLOC || type == PQG_ZCOMP)
{
glEndQuery(GL_SAMPLES_PASSED);
}
}
bool PerfQuery::IsFlushed() const
{
if (!ShouldEmulate())
return true;
return 0 == m_query_count;
}
void PerfQuery::FlushOne()
{
if (!ShouldEmulate())
return;
auto& entry = m_query_buffer[m_query_read_pos];
GLuint result = 0;
glGetQueryObjectuiv(entry.query_id, GL_QUERY_RESULT, &result);
// NOTE: Reported pixel metrics should be referenced to native resolution
m_results[entry.query_type] += (u64)result * EFB_WIDTH / g_renderer->GetTargetWidth() * EFB_HEIGHT / g_renderer->GetTargetHeight();
m_query_read_pos = (m_query_read_pos + 1) % ArraySize(m_query_buffer);
--m_query_count;
}
// TODO: could selectively flush things, but I don't think that will do much
void PerfQuery::FlushResults()
{
if (!ShouldEmulate())
return;
while (!IsFlushed())
FlushOne();
}
void PerfQuery::WeakFlush()
{
if (!ShouldEmulate())
return;
while (!IsFlushed())
{
auto& entry = m_query_buffer[m_query_read_pos];
GLuint result = GL_FALSE;
glGetQueryObjectuiv(entry.query_id, GL_QUERY_RESULT_AVAILABLE, &result);
if (GL_TRUE == result)
{
FlushOne();
}
else
{
break;
}
}
}
void PerfQuery::ResetQuery()
{
m_query_count = 0;
std::fill_n(m_results, ArraySize(m_results), 0);
}
u32 PerfQuery::GetQueryResult(PerfQueryType type)
{
if (!ShouldEmulate())
return 0;
u32 result = 0;
if (type == PQ_ZCOMP_INPUT_ZCOMPLOC || type == PQ_ZCOMP_OUTPUT_ZCOMPLOC)
{
result = m_results[PQG_ZCOMP_ZCOMPLOC];
}
else if (type == PQ_ZCOMP_INPUT || type == PQ_ZCOMP_OUTPUT)
{
result = m_results[PQG_ZCOMP];
}
else if (type == PQ_BLEND_INPUT)
{
result = m_results[PQG_ZCOMP] + m_results[PQG_ZCOMP_ZCOMPLOC];
}
else if (type == PQ_EFB_COPY_CLOCKS)
{
result = m_results[PQG_EFB_COPY_CLOCKS];
}
return result / 4;
}
} // namespace

View File

@ -0,0 +1,46 @@
#ifndef _PERFQUERY_H_
#define _PERFQUERY_H_
#include "PerfQueryBase.h"
namespace OGL {
class PerfQuery : public PerfQueryBase
{
public:
PerfQuery();
~PerfQuery();
void EnableQuery(PerfQueryGroup type);
void DisableQuery(PerfQueryGroup type);
void ResetQuery();
u32 GetQueryResult(PerfQueryType type);
void FlushResults();
bool IsFlushed() const;
private:
struct ActiveQuery
{
GLuint query_id;
PerfQueryGroup query_type;
};
// when testing in SMS: 64 was too small, 128 was ok
static const u32 PERF_QUERY_BUFFER_SIZE = 512;
void WeakFlush();
// Only use when non-empty
void FlushOne();
// This contains gl query objects with unretrieved results.
ActiveQuery m_query_buffer[PERF_QUERY_BUFFER_SIZE];
u32 m_query_read_pos;
// TODO: sloppy
volatile u32 m_query_count;
volatile u32 m_results[PQG_NUM_MEMBERS];
};
} // namespace
#endif // _PERFQUERY_H_

View File

@ -0,0 +1,104 @@
// Copyright 2013 Dolphin Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.
#include "Globals.h"
#include "GLUtil.h"
#include <cmath>
#include "Statistics.h"
#include "VideoConfig.h"
#include "ImageWrite.h"
#include "Common.h"
#include "Render.h"
#include "VertexShaderGen.h"
#include "ProgramShaderCache.h"
#include "PixelShaderManager.h"
#include "OnScreenDisplay.h"
#include "StringUtil.h"
#include "FileUtil.h"
#include "Debugger.h"
namespace OGL
{
void SetPSConstant4fvByName(const char * name, unsigned int offset, const float *f, const unsigned int count = 1)
{
ProgramShaderCache::PCacheEntry tmp = ProgramShaderCache::GetShaderProgram();
for (int a = 0; a < NUM_UNIFORMS; ++a)
{
if (!strcmp(name, UniformNames[a]))
{
if (tmp.shader.UniformLocations[a] == -1)
return;
else if (tmp.shader.UniformSize[a] <= offset)
return;
else
{
unsigned int maxcount= tmp.shader.UniformSize[a]-offset;
glUniform4fv(tmp.shader.UniformLocations[a] + offset, std::min(count, maxcount), f);
return;
}
}
}
}
// Renderer functions
void Renderer::SetPSConstant4f(unsigned int const_number, float f1, float f2, float f3, float f4)
{
float const f[4] = {f1, f2, f3, f4};
if (g_ActiveConfig.backend_info.bSupportsGLSLUBO)
{
ProgramShaderCache::SetMultiPSConstant4fv(const_number, f, 1);
return;
}
for (unsigned int a = 0; a < 10; ++a)
{
if (const_number >= PSVar_Loc[a].reg && const_number < (PSVar_Loc[a].reg + PSVar_Loc[a].size))
{
unsigned int offset = const_number - PSVar_Loc[a].reg;
SetPSConstant4fvByName(PSVar_Loc[a].name, offset, f);
return;
}
}
}
void Renderer::SetPSConstant4fv(unsigned int const_number, const float *f)
{
if (g_ActiveConfig.backend_info.bSupportsGLSLUBO)
{
ProgramShaderCache::SetMultiPSConstant4fv(const_number, f, 1);
return;
}
for (unsigned int a = 0; a < 10; ++a)
{
if (const_number >= PSVar_Loc[a].reg && const_number < (PSVar_Loc[a].reg + PSVar_Loc[a].size))
{
unsigned int offset = const_number - PSVar_Loc[a].reg;
SetPSConstant4fvByName(PSVar_Loc[a].name, offset, f);
return;
}
}
}
void Renderer::SetMultiPSConstant4fv(unsigned int const_number, unsigned int count, const float *f)
{
if (g_ActiveConfig.backend_info.bSupportsGLSLUBO)
{
ProgramShaderCache::SetMultiPSConstant4fv(const_number, f, count);
return;
}
for (unsigned int a = 0; a < 10; ++a)
{
if (const_number >= PSVar_Loc[a].reg && const_number < (PSVar_Loc[a].reg + PSVar_Loc[a].size))
{
unsigned int offset = const_number - PSVar_Loc[a].reg;
SetPSConstant4fvByName(PSVar_Loc[a].name, offset, f, count);
return;
}
}
}
} // namespace OGL

View File

@ -0,0 +1,180 @@
// Copyright 2013 Dolphin Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.
#include "CommonPaths.h"
#include "FileUtil.h"
#include "VideoCommon.h"
#include "VideoConfig.h"
#include "GLUtil.h"
#include "PostProcessing.h"
#include "ProgramShaderCache.h"
#include "FramebufferManager.h"
namespace OGL
{
namespace PostProcessing
{
static std::string s_currentShader;
static SHADER s_shader;
static bool s_enable;
static u32 s_width;
static u32 s_height;
static GLuint s_fbo;
static GLuint s_texture;
static GLuint s_vao;
static GLuint s_vbo;
static GLuint s_uniform_resolution;
static char s_vertex_shader[] =
"in vec2 rawpos;\n"
"in vec2 tex0;\n"
"out vec2 uv0;\n"
"void main(void) {\n"
" gl_Position = vec4(rawpos,0,1);\n"
" uv0 = tex0;\n"
"}\n";
void Init()
{
s_currentShader = "";
s_enable = 0;
s_width = 0;
s_height = 0;
glGenFramebuffers(1, &s_fbo);
glGenTextures(1, &s_texture);
glBindTexture(GL_TEXTURE_2D, s_texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); // disable mipmaps
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glBindFramebuffer(GL_FRAMEBUFFER, s_fbo);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, s_texture, 0);
FramebufferManager::SetFramebuffer(0);
glGenBuffers(1, &s_vbo);
glBindBuffer(GL_ARRAY_BUFFER, s_vbo);
GLfloat vertices[] = {
-1.f, -1.f, 0.f, 0.f,
-1.f, 1.f, 0.f, 1.f,
1.f, -1.f, 1.f, 0.f,
1.f, 1.f, 1.f, 1.f
};
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glGenVertexArrays(1, &s_vao);
glBindVertexArray( s_vao );
glEnableVertexAttribArray(SHADER_POSITION_ATTRIB);
glVertexAttribPointer(SHADER_POSITION_ATTRIB, 2, GL_FLOAT, 0, sizeof(GLfloat)*4, NULL);
glEnableVertexAttribArray(SHADER_TEXTURE0_ATTRIB);
glVertexAttribPointer(SHADER_TEXTURE0_ATTRIB, 2, GL_FLOAT, 0, sizeof(GLfloat)*4, (GLfloat*)NULL+2);
}
void Shutdown()
{
s_shader.Destroy();
glDeleteFramebuffers(1, &s_vbo);
glDeleteTextures(1, &s_texture);
glDeleteBuffers(1, &s_vbo);
glDeleteVertexArrays(1, &s_vao);
}
void ReloadShader()
{
s_currentShader = "";
}
void BindTargetFramebuffer ()
{
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, s_enable ? s_fbo : 0);
}
void BlitToScreen()
{
if(!s_enable) return;
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glViewport(0, 0, s_width, s_height);
glBindVertexArray(s_vao);
s_shader.Bind();
glUniform4f(s_uniform_resolution, (float)s_width, (float)s_height, 1.0f/(float)s_width, 1.0f/(float)s_height);
glActiveTexture(GL_TEXTURE0+9);
glBindTexture(GL_TEXTURE_2D, s_texture);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glBindTexture(GL_TEXTURE_2D, 0);
/* glBindFramebuffer(GL_READ_FRAMEBUFFER, s_fbo);
glBlitFramebuffer(rc.left, rc.bottom, rc.right, rc.top,
rc.left, rc.bottom, rc.right, rc.top,
GL_COLOR_BUFFER_BIT, GL_NEAREST);*/
}
void Update ( u32 width, u32 height )
{
ApplyShader();
if(s_enable && (width != s_width || height != s_height)) {
s_width = width;
s_height = height;
// alloc texture for framebuffer
glActiveTexture(GL_TEXTURE0+9);
glBindTexture(GL_TEXTURE_2D, s_texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glBindTexture(GL_TEXTURE_2D, 0);
}
}
void ApplyShader()
{
// shader didn't changed
if (s_currentShader == g_ActiveConfig.sPostProcessingShader) return;
s_currentShader = g_ActiveConfig.sPostProcessingShader;
s_enable = false;
s_shader.Destroy();
// shader disabled
if (g_ActiveConfig.sPostProcessingShader == "") return;
// so need to compile shader
// loading shader code
std::string code;
std::string path = File::GetUserPath(D_SHADERS_IDX) + g_ActiveConfig.sPostProcessingShader + ".glsl";
if (!File::Exists(path))
{
// Fallback to shared user dir
path = File::GetSysDirectory() + SHADERS_DIR DIR_SEP + g_ActiveConfig.sPostProcessingShader + ".glsl";
}
if(!File::ReadFileToString(true, path.c_str(), code)) {
ERROR_LOG(VIDEO, "Post-processing shader not found: %s", path.c_str());
return;
}
// and compile it
if (!ProgramShaderCache::CompileShader(s_shader, s_vertex_shader, code.c_str())) {
ERROR_LOG(VIDEO, "Failed to compile post-processing shader %s", s_currentShader.c_str());
return;
}
// read uniform locations
s_uniform_resolution = glGetUniformLocation(s_shader.glprogid, "resolution");
// successful
s_enable = true;
}
} // namespace
} // namespace OGL

View File

@ -0,0 +1,32 @@
// Copyright 2013 Dolphin Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.
#ifndef _POSTPROCESSING_H_
#define _POSTPROCESSING_H_
#include "VideoCommon.h"
#include "GLUtil.h"
namespace OGL
{
namespace PostProcessing
{
void Init();
void Shutdown();
void BindTargetFramebuffer();
void BlitToScreen();
void Update(u32 width, u32 height);
void ReloadShader();
void ApplyShader();
} // namespace
} // namespace
#endif // _POSTPROCESSING_H_

View File

@ -0,0 +1,610 @@
// Copyright 2013 Dolphin Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.
#include "ProgramShaderCache.h"
#include "DriverDetails.h"
#include "MathUtil.h"
#include "StreamBuffer.h"
#include "Debugger.h"
#include "Statistics.h"
#include "ImageWrite.h"
#include "Render.h"
namespace OGL
{
static const u32 UBO_LENGTH = 32*1024*1024;
GLintptr ProgramShaderCache::s_vs_data_size;
GLintptr ProgramShaderCache::s_ps_data_size;
GLintptr ProgramShaderCache::s_vs_data_offset;
u8 *ProgramShaderCache::s_ubo_buffer;
u32 ProgramShaderCache::s_ubo_buffer_size;
bool ProgramShaderCache::s_ubo_dirty;
static StreamBuffer *s_buffer;
static int num_failures = 0;
LinearDiskCache<SHADERUID, u8> g_program_disk_cache;
static GLuint CurrentProgram = 0;
ProgramShaderCache::PCache ProgramShaderCache::pshaders;
ProgramShaderCache::PCacheEntry* ProgramShaderCache::last_entry;
SHADERUID ProgramShaderCache::last_uid;
UidChecker<PixelShaderUid,PixelShaderCode> ProgramShaderCache::pixel_uid_checker;
UidChecker<VertexShaderUid,VertexShaderCode> ProgramShaderCache::vertex_uid_checker;
static char s_glsl_header[1024] = "";
const char *UniformNames[NUM_UNIFORMS] =
{
// PIXEL SHADER UNIFORMS
I_COLORS,
I_KCOLORS,
I_ALPHA,
I_TEXDIMS,
I_ZBIAS ,
I_INDTEXSCALE ,
I_INDTEXMTX,
I_FOG,
I_PLIGHTS,
I_PMATERIALS,
// VERTEX SHADER UNIFORMS
I_POSNORMALMATRIX,
I_PROJECTION ,
I_MATERIALS,
I_LIGHTS,
I_TEXMATRICES,
I_TRANSFORMMATRICES ,
I_NORMALMATRICES ,
I_POSTTRANSFORMMATRICES,
I_DEPTHPARAMS,
};
void SHADER::SetProgramVariables()
{
// glsl shader must be bind to set samplers
Bind();
// Bind UBO
if (g_ActiveConfig.backend_info.bSupportsGLSLUBO)
{
GLint PSBlock_id = glGetUniformBlockIndex(glprogid, "PSBlock");
GLint VSBlock_id = glGetUniformBlockIndex(glprogid, "VSBlock");
if(PSBlock_id != -1)
glUniformBlockBinding(glprogid, PSBlock_id, 1);
if(VSBlock_id != -1)
glUniformBlockBinding(glprogid, VSBlock_id, 2);
}
// UBO workaround
for (int a = 0; a < NUM_UNIFORMS; ++a)
{
UniformLocations[a] = glGetUniformLocation(glprogid, UniformNames[a]);
UniformSize[a] = 0;
if(g_ActiveConfig.backend_info.bSupportsGLSLUBO)
break;
}
if(!g_ActiveConfig.backend_info.bSupportsGLSLUBO)
{
int max_uniforms = 0;
char name[50];
int size;
glGetProgramiv(glprogid, GL_ACTIVE_UNIFORMS, &max_uniforms);
for(int i=0; i<max_uniforms; i++)
{
glGetActiveUniform(glprogid, i, sizeof(name), NULL, &size, NULL, name);
for (int a = 0; a < NUM_UNIFORMS; ++a)
{
if(strstr(name, UniformNames[a]))
{
UniformSize[a] = size;
break;
}
}
}
}
// Bind Texture Sampler
for (int a = 0; a <= 9; ++a)
{
char name[8];
snprintf(name, 8, "samp%d", a);
// Still need to get sampler locations since we aren't binding them statically in the shaders
int loc = glGetUniformLocation(glprogid, name);
if (loc != -1)
glUniform1i(loc, a);
}
}
void SHADER::SetProgramBindings()
{
if (g_ActiveConfig.backend_info.bSupportsDualSourceBlend)
{
// So we do support extended blending
// So we need to set a few more things here.
// Bind our out locations
#ifndef USE_GLES3
glBindFragDataLocationIndexed(glprogid, 0, 0, "ocol0");
glBindFragDataLocationIndexed(glprogid, 0, 1, "ocol1");
#endif
}
// Need to set some attribute locations
glBindAttribLocation(glprogid, SHADER_POSITION_ATTRIB, "rawpos");
glBindAttribLocation(glprogid, SHADER_POSMTX_ATTRIB, "fposmtx");
glBindAttribLocation(glprogid, SHADER_COLOR0_ATTRIB, "color0");
glBindAttribLocation(glprogid, SHADER_COLOR1_ATTRIB, "color1");
glBindAttribLocation(glprogid, SHADER_NORM0_ATTRIB, "rawnorm0");
glBindAttribLocation(glprogid, SHADER_NORM1_ATTRIB, "rawnorm1");
glBindAttribLocation(glprogid, SHADER_NORM2_ATTRIB, "rawnorm2");
for(int i=0; i<8; i++) {
char attrib_name[8];
snprintf(attrib_name, 8, "tex%d", i);
glBindAttribLocation(glprogid, SHADER_TEXTURE0_ATTRIB+i, attrib_name);
}
}
void SHADER::Bind()
{
if(CurrentProgram != glprogid)
{
glUseProgram(glprogid);
CurrentProgram = glprogid;
}
}
void ProgramShaderCache::SetMultiPSConstant4fv(unsigned int offset, const float *f, unsigned int count)
{
s_ubo_dirty = true;
memcpy(s_ubo_buffer+(offset*4*sizeof(float)), f, count*4*sizeof(float));
}
void ProgramShaderCache::SetMultiVSConstant4fv(unsigned int offset, const float *f, unsigned int count)
{
s_ubo_dirty = true;
memcpy(s_ubo_buffer+(offset*4*sizeof(float))+s_vs_data_offset, f, count*4*sizeof(float));
}
void ProgramShaderCache::UploadConstants()
{
if(s_ubo_dirty) {
s_buffer->Alloc(s_ubo_buffer_size);
size_t offset = s_buffer->Upload(s_ubo_buffer, s_ubo_buffer_size);
glBindBufferRange(GL_UNIFORM_BUFFER, 1, s_buffer->getBuffer(), offset, s_ps_data_size);
glBindBufferRange(GL_UNIFORM_BUFFER, 2, s_buffer->getBuffer(), offset + s_vs_data_offset, s_vs_data_size);
s_ubo_dirty = false;
ADDSTAT(stats.thisFrame.bytesUniformStreamed, s_ubo_buffer_size);
}
}
GLuint ProgramShaderCache::GetCurrentProgram(void)
{
return CurrentProgram;
}
SHADER* ProgramShaderCache::SetShader ( DSTALPHA_MODE dstAlphaMode, u32 components )
{
SHADERUID uid;
GetShaderId(&uid, dstAlphaMode, components);
// Check if the shader is already set
if (last_entry)
{
if (uid == last_uid)
{
GFX_DEBUGGER_PAUSE_AT(NEXT_PIXEL_SHADER_CHANGE, true);
last_entry->shader.Bind();
return &last_entry->shader;
}
}
last_uid = uid;
// Check if shader is already in cache
PCache::iterator iter = pshaders.find(uid);
if (iter != pshaders.end())
{
PCacheEntry *entry = &iter->second;
last_entry = entry;
GFX_DEBUGGER_PAUSE_AT(NEXT_PIXEL_SHADER_CHANGE, true);
last_entry->shader.Bind();
return &last_entry->shader;
}
// Make an entry in the table
PCacheEntry& newentry = pshaders[uid];
last_entry = &newentry;
newentry.in_cache = 0;
VertexShaderCode vcode;
PixelShaderCode pcode;
GenerateVertexShaderCode(vcode, components, API_OPENGL);
GeneratePixelShaderCode(pcode, dstAlphaMode, API_OPENGL, components);
if (g_ActiveConfig.bEnableShaderDebugging)
{
newentry.shader.strvprog = vcode.GetBuffer();
newentry.shader.strpprog = pcode.GetBuffer();
}
#if defined(_DEBUG) || defined(DEBUGFAST)
if (g_ActiveConfig.iLog & CONF_SAVESHADERS) {
static int counter = 0;
char szTemp[MAX_PATH];
sprintf(szTemp, "%svs_%04i.txt", File::GetUserPath(D_DUMP_IDX).c_str(), counter++);
SaveData(szTemp, vcode.GetBuffer());
sprintf(szTemp, "%sps_%04i.txt", File::GetUserPath(D_DUMP_IDX).c_str(), counter++);
SaveData(szTemp, pcode.GetBuffer());
}
#endif
if (!CompileShader(newentry.shader, vcode.GetBuffer(), pcode.GetBuffer())) {
GFX_DEBUGGER_PAUSE_AT(NEXT_ERROR, true);
return NULL;
}
INCSTAT(stats.numPixelShadersCreated);
SETSTAT(stats.numPixelShadersAlive, pshaders.size());
GFX_DEBUGGER_PAUSE_AT(NEXT_PIXEL_SHADER_CHANGE, true);
last_entry->shader.Bind();
return &last_entry->shader;
}
bool ProgramShaderCache::CompileShader ( SHADER& shader, const char* vcode, const char* pcode )
{
GLuint vsid = CompileSingleShader(GL_VERTEX_SHADER, vcode);
GLuint psid = CompileSingleShader(GL_FRAGMENT_SHADER, pcode);
if(!vsid || !psid)
{
glDeleteShader(vsid);
glDeleteShader(psid);
return false;
}
GLuint pid = shader.glprogid = glCreateProgram();;
glAttachShader(pid, vsid);
glAttachShader(pid, psid);
if (g_ogl_config.bSupportsGLSLCache)
glProgramParameteri(pid, GL_PROGRAM_BINARY_RETRIEVABLE_HINT, GL_TRUE);
shader.SetProgramBindings();
glLinkProgram(pid);
// original shaders aren't needed any more
glDeleteShader(vsid);
glDeleteShader(psid);
GLint linkStatus;
glGetProgramiv(pid, GL_LINK_STATUS, &linkStatus);
GLsizei length = 0;
glGetProgramiv(pid, GL_INFO_LOG_LENGTH, &length);
if (linkStatus != GL_TRUE || (length > 1 && DEBUG_GLSL))
{
GLsizei charsWritten;
GLchar* infoLog = new GLchar[length];
glGetProgramInfoLog(pid, length, &charsWritten, infoLog);
ERROR_LOG(VIDEO, "Program info log:\n%s", infoLog);
char szTemp[MAX_PATH];
sprintf(szTemp, "%sbad_p_%d.txt", File::GetUserPath(D_DUMP_IDX).c_str(), num_failures++);
std::ofstream file;
OpenFStream(file, szTemp, std::ios_base::out);
file << s_glsl_header << vcode << s_glsl_header << pcode << infoLog;
file.close();
if(linkStatus != GL_TRUE)
PanicAlert("Failed to link shaders!\nThis usually happens when trying to use Dolphin with an outdated GPU or integrated GPU like the Intel GMA series.\n\nIf you're sure this is Dolphin's error anyway, post the contents of %s along with this error message at the forums.\n\nDebug info (%s, %s, %s):\n%s",
szTemp,
g_ogl_config.gl_vendor,
g_ogl_config.gl_renderer,
g_ogl_config.gl_version,
infoLog);
delete [] infoLog;
}
if (linkStatus != GL_TRUE)
{
// Compile failed
ERROR_LOG(VIDEO, "Program linking failed; see info log");
// Don't try to use this shader
glDeleteProgram(pid);
return false;
}
shader.SetProgramVariables();
return true;
}
GLuint ProgramShaderCache::CompileSingleShader (GLuint type, const char* code )
{
GLuint result = glCreateShader(type);
const char *src[] = {s_glsl_header, code};
glShaderSource(result, 2, src, NULL);
glCompileShader(result);
GLint compileStatus;
glGetShaderiv(result, GL_COMPILE_STATUS, &compileStatus);
GLsizei length = 0;
glGetShaderiv(result, GL_INFO_LOG_LENGTH, &length);
if (DriverDetails::HasBug(DriverDetails::BUG_BROKENINFOLOG))
length = 1024;
if (compileStatus != GL_TRUE || (length > 1 && DEBUG_GLSL))
{
GLsizei charsWritten;
GLchar* infoLog = new GLchar[length];
glGetShaderInfoLog(result, length, &charsWritten, infoLog);
ERROR_LOG(VIDEO, "PS Shader info log:\n%s", infoLog);
char szTemp[MAX_PATH];
sprintf(szTemp,
"%sbad_%s_%04i.txt",
File::GetUserPath(D_DUMP_IDX).c_str(),
type==GL_VERTEX_SHADER ? "vs" : "ps",
num_failures++);
std::ofstream file;
OpenFStream(file, szTemp, std::ios_base::out);
file << s_glsl_header << code << infoLog;
file.close();
if(compileStatus != GL_TRUE)
PanicAlert("Failed to compile %s shader!\nThis usually happens when trying to use Dolphin with an outdated GPU or integrated GPU like the Intel GMA series.\n\nIf you're sure this is Dolphin's error anyway, post the contents of %s along with this error message at the forums.\n\nDebug info (%s, %s, %s):\n%s",
type==GL_VERTEX_SHADER ? "vertex" : "pixel",
szTemp,
g_ogl_config.gl_vendor,
g_ogl_config.gl_renderer,
g_ogl_config.gl_version,
infoLog);
delete[] infoLog;
}
if (compileStatus != GL_TRUE)
{
// Compile failed
ERROR_LOG(VIDEO, "Shader compilation failed; see info log");
// Don't try to use this shader
glDeleteShader(result);
return 0;
}
(void)GL_REPORT_ERROR();
return result;
}
void ProgramShaderCache::GetShaderId(SHADERUID* uid, DSTALPHA_MODE dstAlphaMode, u32 components)
{
GetPixelShaderUid(uid->puid, dstAlphaMode, API_OPENGL, components);
GetVertexShaderUid(uid->vuid, components, API_OPENGL);
if (g_ActiveConfig.bEnableShaderDebugging)
{
PixelShaderCode pcode;
GeneratePixelShaderCode(pcode, dstAlphaMode, API_OPENGL, components);
pixel_uid_checker.AddToIndexAndCheck(pcode, uid->puid, "Pixel", "p");
VertexShaderCode vcode;
GenerateVertexShaderCode(vcode, components, API_OPENGL);
vertex_uid_checker.AddToIndexAndCheck(vcode, uid->vuid, "Vertex", "v");
}
}
ProgramShaderCache::PCacheEntry ProgramShaderCache::GetShaderProgram(void)
{
return *last_entry;
}
void ProgramShaderCache::Init(void)
{
// We have to get the UBO alignment here because
// if we generate a buffer that isn't aligned
// then the UBO will fail.
if (g_ActiveConfig.backend_info.bSupportsGLSLUBO)
{
GLint Align;
glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &Align);
s_ps_data_size = C_PENVCONST_END * sizeof(float) * 4;
s_vs_data_size = C_VENVCONST_END * sizeof(float) * 4;
s_vs_data_offset = ROUND_UP(s_ps_data_size, Align);
s_ubo_buffer_size = ROUND_UP(s_ps_data_size, Align) + ROUND_UP(s_vs_data_size, Align);
// We multiply by *4*4 because we need to get down to basic machine units.
// So multiply by four to get how many floats we have from vec4s
// Then once more to get bytes
s_buffer = new StreamBuffer(GL_UNIFORM_BUFFER, UBO_LENGTH);
s_ubo_buffer = new u8[s_ubo_buffer_size];
memset(s_ubo_buffer, 0, s_ubo_buffer_size);
s_ubo_dirty = true;
}
// Read our shader cache, only if supported
if (g_ogl_config.bSupportsGLSLCache)
{
GLint Supported;
glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS, &Supported);
if(!Supported)
{
ERROR_LOG(VIDEO, "GL_ARB_get_program_binary is supported, but no binary format is known. So disable shader cache.");
g_ogl_config.bSupportsGLSLCache = false;
}
else
{
if (!File::Exists(File::GetUserPath(D_SHADERCACHE_IDX)))
File::CreateDir(File::GetUserPath(D_SHADERCACHE_IDX).c_str());
char cache_filename[MAX_PATH];
sprintf(cache_filename, "%sogl-%s-shaders.cache", File::GetUserPath(D_SHADERCACHE_IDX).c_str(),
SConfig::GetInstance().m_LocalCoreStartupParameter.m_strUniqueID.c_str());
ProgramShaderCacheInserter inserter;
g_program_disk_cache.OpenAndRead(cache_filename, inserter);
}
SETSTAT(stats.numPixelShadersAlive, pshaders.size());
}
CreateHeader();
CurrentProgram = 0;
last_entry = NULL;
}
void ProgramShaderCache::Shutdown(void)
{
// store all shaders in cache on disk
if (g_ogl_config.bSupportsGLSLCache)
{
PCache::iterator iter = pshaders.begin();
for (; iter != pshaders.end(); ++iter)
{
if(iter->second.in_cache) continue;
GLint binary_size;
glGetProgramiv(iter->second.shader.glprogid, GL_PROGRAM_BINARY_LENGTH, &binary_size);
if(!binary_size) continue;
u8 *data = new u8[binary_size+sizeof(GLenum)];
u8 *binary = data + sizeof(GLenum);
GLenum *prog_format = (GLenum*)data;
glGetProgramBinary(iter->second.shader.glprogid, binary_size, NULL, prog_format, binary);
g_program_disk_cache.Append(iter->first, data, binary_size+sizeof(GLenum));
delete [] data;
}
g_program_disk_cache.Sync();
g_program_disk_cache.Close();
}
glUseProgram(0);
PCache::iterator iter = pshaders.begin();
for (; iter != pshaders.end(); ++iter)
iter->second.Destroy();
pshaders.clear();
pixel_uid_checker.Invalidate();
vertex_uid_checker.Invalidate();
if (g_ActiveConfig.backend_info.bSupportsGLSLUBO)
{
delete s_buffer;
s_buffer = 0;
delete [] s_ubo_buffer;
s_ubo_buffer = 0;
}
}
void ProgramShaderCache::CreateHeader ( void )
{
GLSL_VERSION v = g_ogl_config.eSupportedGLSLVersion;
snprintf(s_glsl_header, sizeof(s_glsl_header),
"%s\n"
"%s\n" // ubo
"%s\n" // early-z
// Precision defines for GLSLES2/3
"%s\n"
"\n"// A few required defines and ones that will make our lives a lot easier
"#define ATTRIN %s\n"
"#define ATTROUT %s\n"
"#define VARYIN %s\n"
"#define VARYOUT %s\n"
// Silly differences
"#define float2 vec2\n"
"#define float3 vec3\n"
"#define float4 vec4\n"
// hlsl to glsl function translation
"#define frac fract\n"
"#define lerp mix\n"
// texture2d hack
"%s\n"
"%s\n"
"%s\n"
// GLSLES2 hacks
"%s\n"
"%s\n"
"%s\n"
"%s\n"
"%s\n"
"%s\n"
"%s\n"
"#define COLOROUT(name) %s\n"
, v==GLSLES2 ? "" : v==GLSLES3 ? "#version 300 es" : v==GLSL_130 ? "#version 130" : v==GLSL_140 ? "#version 140" : "#version 150"
, g_ActiveConfig.backend_info.bSupportsGLSLUBO && v<GLSL_140 ? "#extension GL_ARB_uniform_buffer_object : enable" : ""
, g_ActiveConfig.backend_info.bSupportsEarlyZ ? "#extension GL_ARB_shader_image_load_store : enable" : ""
, (v==GLSLES3 || v==GLSLES2) ? "precision highp float;" : ""
, v==GLSLES2 ? "attribute" : "in"
, v==GLSLES2 ? "attribute" : "out"
, v==GLSLES2 ? "varying" : DriverDetails::HasBug(DriverDetails::BUG_BROKENCENTROID) ? "in" : "centroid in"
, v==GLSLES2 ? "varying" : DriverDetails::HasBug(DriverDetails::BUG_BROKENCENTROID) ? "out" : "centroid out"
, v==GLSLES2 ? "#define texture2DRect texture2D" : v==GLSLES3 ? "" : v<=GLSL_130 ? "#extension GL_ARB_texture_rectangle : enable" : "#define texture2DRect texture"
, v==GLSLES3 ? "#define texture2DRect(samp, uv) texelFetch(samp, ivec2(floor(uv)), 0)" : ""
, (v==GLSLES3 || v==GLSLES2) ? "#define sampler2DRect sampler2D" : ""
, v==GLSLES2 ? "#define texture texture2D" : ""
, v==GLSLES2 ? "#define round(x) floor((x)+0.5)" : ""
, v==GLSLES2 ? "#define out " : ""
, v==GLSLES2 ? "#define ocol0 gl_FragColor" : ""
, v==GLSLES2 ? "#define ocol1 gl_FragColor" : ""
, DriverDetails::HasBug(DriverDetails::BUG_ISTEGRA) ? "#extension GL_NV_uniform_buffer_object : enable" : ""
, DriverDetails::HasBug(DriverDetails::BUG_ISTEGRA) ? "#extension GL_NV_fragdepth : enable" : ""
, v==GLSLES2 ? "" : "out vec4 name;"
);
}
void ProgramShaderCache::ProgramShaderCacheInserter::Read ( const SHADERUID& key, const u8* value, u32 value_size )
{
const u8 *binary = value+sizeof(GLenum);
GLenum *prog_format = (GLenum*)value;
GLint binary_size = value_size-sizeof(GLenum);
PCacheEntry entry;
entry.in_cache = 1;
entry.shader.glprogid = glCreateProgram();
glProgramBinary(entry.shader.glprogid, *prog_format, binary, binary_size);
GLint success;
glGetProgramiv(entry.shader.glprogid, GL_LINK_STATUS, &success);
if (success)
{
pshaders[key] = entry;
entry.shader.SetProgramVariables();
}
else
glDeleteProgram(entry.shader.glprogid);
}
} // namespace OGL

View File

@ -0,0 +1,122 @@
// Copyright 2013 Dolphin Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.
#ifndef PROGRAM_SHADER_CACHE_H_
#define PROGRAM_SHADER_CACHE_H_
#include "GLUtil.h"
#include "PixelShaderGen.h"
#include "VertexShaderGen.h"
#include "LinearDiskCache.h"
#include "ConfigManager.h"
namespace OGL
{
class SHADERUID
{
public:
VertexShaderUid vuid;
PixelShaderUid puid;
SHADERUID() {}
SHADERUID(const SHADERUID& r) : vuid(r.vuid), puid(r.puid) {}
bool operator <(const SHADERUID& r) const
{
if(puid < r.puid) return true;
if(r.puid < puid) return false;
if(vuid < r.vuid) return true;
return false;
}
bool operator ==(const SHADERUID& r) const
{
return puid == r.puid && vuid == r.vuid;
}
};
const int NUM_UNIFORMS = 19;
extern const char *UniformNames[NUM_UNIFORMS];
struct SHADER
{
SHADER() : glprogid(0) { }
void Destroy()
{
glDeleteProgram(glprogid);
glprogid = 0;
}
GLuint glprogid; // opengl program id
std::string strvprog, strpprog;
GLint UniformLocations[NUM_UNIFORMS];
u32 UniformSize[NUM_UNIFORMS];
void SetProgramVariables();
void SetProgramBindings();
void Bind();
};
class ProgramShaderCache
{
public:
struct PCacheEntry
{
SHADER shader;
bool in_cache;
void Destroy()
{
shader.Destroy();
}
};
typedef std::map<SHADERUID, PCacheEntry> PCache;
static PCacheEntry GetShaderProgram(void);
static GLuint GetCurrentProgram(void);
static SHADER* SetShader(DSTALPHA_MODE dstAlphaMode, u32 components);
static void GetShaderId(SHADERUID *uid, DSTALPHA_MODE dstAlphaMode, u32 components);
static bool CompileShader(SHADER &shader, const char* vcode, const char* pcode);
static GLuint CompileSingleShader(GLuint type, const char *code);
static void SetMultiPSConstant4fv(unsigned int offset, const float *f, unsigned int count);
static void SetMultiVSConstant4fv(unsigned int offset, const float *f, unsigned int count);
static void UploadConstants();
static void Init(void);
static void Shutdown(void);
static void CreateHeader(void);
private:
class ProgramShaderCacheInserter : public LinearDiskCacheReader<SHADERUID, u8>
{
public:
void Read(const SHADERUID &key, const u8 *value, u32 value_size);
};
static PCache pshaders;
static PCacheEntry* last_entry;
static SHADERUID last_uid;
static UidChecker<PixelShaderUid,PixelShaderCode> pixel_uid_checker;
static UidChecker<VertexShaderUid,VertexShaderCode> vertex_uid_checker;
static GLintptr s_vs_data_size;
static GLintptr s_ps_data_size;
static GLintptr s_vs_data_offset;
static u8 *s_ubo_buffer;
static u32 s_ubo_buffer_size;
static bool s_ubo_dirty;
};
} // namespace OGL
#endif

View File

@ -0,0 +1,270 @@
// Copyright 2013 Dolphin Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.
#include "GLUtil.h"
#include "RasterFont.h"
#include "ProgramShaderCache.h"
// globals
namespace OGL {
static const u32 char_width = 8;
static const u32 char_height = 13;
static const u32 char_offset = 32;
static const u32 char_count = 95;
const u8 rasters[char_count][char_height] = {
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
{0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18},
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x36, 0x36, 0x36},
{0x00, 0x00, 0x00, 0x66, 0x66, 0xff, 0x66, 0x66, 0xff, 0x66, 0x66, 0x00, 0x00},
{0x00, 0x00, 0x18, 0x7e, 0xff, 0x1b, 0x1f, 0x7e, 0xf8, 0xd8, 0xff, 0x7e, 0x18},
{0x00, 0x00, 0x0e, 0x1b, 0xdb, 0x6e, 0x30, 0x18, 0x0c, 0x76, 0xdb, 0xd8, 0x70},
{0x00, 0x00, 0x7f, 0xc6, 0xcf, 0xd8, 0x70, 0x70, 0xd8, 0xcc, 0xcc, 0x6c, 0x38},
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x1c, 0x0c, 0x0e},
{0x00, 0x00, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x18, 0x0c},
{0x00, 0x00, 0x30, 0x18, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x18, 0x30},
{0x00, 0x00, 0x00, 0x00, 0x99, 0x5a, 0x3c, 0xff, 0x3c, 0x5a, 0x99, 0x00, 0x00},
{0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0xff, 0xff, 0x18, 0x18, 0x18, 0x00, 0x00},
{0x00, 0x00, 0x30, 0x18, 0x1c, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00},
{0x00, 0x00, 0x00, 0x38, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
{0x00, 0x60, 0x60, 0x30, 0x30, 0x18, 0x18, 0x0c, 0x0c, 0x06, 0x06, 0x03, 0x03},
{0x00, 0x00, 0x3c, 0x66, 0xc3, 0xe3, 0xf3, 0xdb, 0xcf, 0xc7, 0xc3, 0x66, 0x3c},
{0x00, 0x00, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x78, 0x38, 0x18},
{0x00, 0x00, 0xff, 0xc0, 0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x03, 0xe7, 0x7e},
{0x00, 0x00, 0x7e, 0xe7, 0x03, 0x03, 0x07, 0x7e, 0x07, 0x03, 0x03, 0xe7, 0x7e},
{0x00, 0x00, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xff, 0xcc, 0x6c, 0x3c, 0x1c, 0x0c},
{0x00, 0x00, 0x7e, 0xe7, 0x03, 0x03, 0x07, 0xfe, 0xc0, 0xc0, 0xc0, 0xc0, 0xff},
{0x00, 0x00, 0x7e, 0xe7, 0xc3, 0xc3, 0xc7, 0xfe, 0xc0, 0xc0, 0xc0, 0xe7, 0x7e},
{0x00, 0x00, 0x30, 0x30, 0x30, 0x30, 0x18, 0x0c, 0x06, 0x03, 0x03, 0x03, 0xff},
{0x00, 0x00, 0x7e, 0xe7, 0xc3, 0xc3, 0xe7, 0x7e, 0xe7, 0xc3, 0xc3, 0xe7, 0x7e},
{0x00, 0x00, 0x7e, 0xe7, 0x03, 0x03, 0x03, 0x7f, 0xe7, 0xc3, 0xc3, 0xe7, 0x7e},
{0x00, 0x00, 0x00, 0x38, 0x38, 0x00, 0x00, 0x38, 0x38, 0x00, 0x00, 0x00, 0x00},
{0x00, 0x00, 0x30, 0x18, 0x1c, 0x1c, 0x00, 0x00, 0x1c, 0x1c, 0x00, 0x00, 0x00},
{0x00, 0x00, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06},
{0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00},
{0x00, 0x00, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x03, 0x06, 0x0c, 0x18, 0x30, 0x60},
{0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x18, 0x0c, 0x06, 0x03, 0xc3, 0xc3, 0x7e},
{0x00, 0x00, 0x3f, 0x60, 0xcf, 0xdb, 0xd3, 0xdd, 0xc3, 0x7e, 0x00, 0x00, 0x00},
{0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xff, 0xc3, 0xc3, 0xc3, 0x66, 0x3c, 0x18},
{0x00, 0x00, 0xfe, 0xc7, 0xc3, 0xc3, 0xc7, 0xfe, 0xc7, 0xc3, 0xc3, 0xc7, 0xfe},
{0x00, 0x00, 0x7e, 0xe7, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xe7, 0x7e},
{0x00, 0x00, 0xfc, 0xce, 0xc7, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc7, 0xce, 0xfc},
{0x00, 0x00, 0xff, 0xc0, 0xc0, 0xc0, 0xc0, 0xfc, 0xc0, 0xc0, 0xc0, 0xc0, 0xff},
{0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xfc, 0xc0, 0xc0, 0xc0, 0xff},
{0x00, 0x00, 0x7e, 0xe7, 0xc3, 0xc3, 0xcf, 0xc0, 0xc0, 0xc0, 0xc0, 0xe7, 0x7e},
{0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xff, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3},
{0x00, 0x00, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e},
{0x00, 0x00, 0x7c, 0xee, 0xc6, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06},
{0x00, 0x00, 0xc3, 0xc6, 0xcc, 0xd8, 0xf0, 0xe0, 0xf0, 0xd8, 0xcc, 0xc6, 0xc3},
{0x00, 0x00, 0xff, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0},
{0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xdb, 0xff, 0xff, 0xe7, 0xc3},
{0x00, 0x00, 0xc7, 0xc7, 0xcf, 0xcf, 0xdf, 0xdb, 0xfb, 0xf3, 0xf3, 0xe3, 0xe3},
{0x00, 0x00, 0x7e, 0xe7, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xe7, 0x7e},
{0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xfe, 0xc7, 0xc3, 0xc3, 0xc7, 0xfe},
{0x00, 0x00, 0x3f, 0x6e, 0xdf, 0xdb, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x66, 0x3c},
{0x00, 0x00, 0xc3, 0xc6, 0xcc, 0xd8, 0xf0, 0xfe, 0xc7, 0xc3, 0xc3, 0xc7, 0xfe},
{0x00, 0x00, 0x7e, 0xe7, 0x03, 0x03, 0x07, 0x7e, 0xe0, 0xc0, 0xc0, 0xe7, 0x7e},
{0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff},
{0x00, 0x00, 0x7e, 0xe7, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3},
{0x00, 0x00, 0x18, 0x3c, 0x3c, 0x66, 0x66, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3},
{0x00, 0x00, 0xc3, 0xe7, 0xff, 0xff, 0xdb, 0xdb, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3},
{0x00, 0x00, 0xc3, 0x66, 0x66, 0x3c, 0x3c, 0x18, 0x3c, 0x3c, 0x66, 0x66, 0xc3},
{0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x3c, 0x66, 0x66, 0xc3},
{0x00, 0x00, 0xff, 0xc0, 0xc0, 0x60, 0x30, 0x7e, 0x0c, 0x06, 0x03, 0x03, 0xff},
{0x00, 0x00, 0x3c, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3c},
{0x00, 0x03, 0x03, 0x06, 0x06, 0x0c, 0x0c, 0x18, 0x18, 0x30, 0x30, 0x60, 0x60},
{0x00, 0x00, 0x3c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x3c},
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0x66, 0x3c, 0x18},
{0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x38, 0x30, 0x70},
{0x00, 0x00, 0x7f, 0xc3, 0xc3, 0x7f, 0x03, 0xc3, 0x7e, 0x00, 0x00, 0x00, 0x00},
{0x00, 0x00, 0xfe, 0xc3, 0xc3, 0xc3, 0xc3, 0xfe, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0},
{0x00, 0x00, 0x7e, 0xc3, 0xc0, 0xc0, 0xc0, 0xc3, 0x7e, 0x00, 0x00, 0x00, 0x00},
{0x00, 0x00, 0x7f, 0xc3, 0xc3, 0xc3, 0xc3, 0x7f, 0x03, 0x03, 0x03, 0x03, 0x03},
{0x00, 0x00, 0x7f, 0xc0, 0xc0, 0xfe, 0xc3, 0xc3, 0x7e, 0x00, 0x00, 0x00, 0x00},
{0x00, 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x30, 0x33, 0x1e},
{0x7e, 0xc3, 0x03, 0x03, 0x7f, 0xc3, 0xc3, 0xc3, 0x7e, 0x00, 0x00, 0x00, 0x00},
{0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xfe, 0xc0, 0xc0, 0xc0, 0xc0},
{0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x18, 0x00},
{0x38, 0x6c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x00, 0x00, 0x0c, 0x00},
{0x00, 0x00, 0xc6, 0xcc, 0xf8, 0xf0, 0xd8, 0xcc, 0xc6, 0xc0, 0xc0, 0xc0, 0xc0},
{0x00, 0x00, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x78},
{0x00, 0x00, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xfe, 0x00, 0x00, 0x00, 0x00},
{0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xfc, 0x00, 0x00, 0x00, 0x00},
{0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00},
{0xc0, 0xc0, 0xc0, 0xfe, 0xc3, 0xc3, 0xc3, 0xc3, 0xfe, 0x00, 0x00, 0x00, 0x00},
{0x03, 0x03, 0x03, 0x7f, 0xc3, 0xc3, 0xc3, 0xc3, 0x7f, 0x00, 0x00, 0x00, 0x00},
{0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xe0, 0xfe, 0x00, 0x00, 0x00, 0x00},
{0x00, 0x00, 0xfe, 0x03, 0x03, 0x7e, 0xc0, 0xc0, 0x7f, 0x00, 0x00, 0x00, 0x00},
{0x00, 0x00, 0x1c, 0x36, 0x30, 0x30, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x30, 0x00},
{0x00, 0x00, 0x7e, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00},
{0x00, 0x00, 0x18, 0x3c, 0x3c, 0x66, 0x66, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00},
{0x00, 0x00, 0xc3, 0xe7, 0xff, 0xdb, 0xc3, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00},
{0x00, 0x00, 0xc3, 0x66, 0x3c, 0x18, 0x3c, 0x66, 0xc3, 0x00, 0x00, 0x00, 0x00},
{0xc0, 0x60, 0x60, 0x30, 0x18, 0x3c, 0x66, 0x66, 0xc3, 0x00, 0x00, 0x00, 0x00},
{0x00, 0x00, 0xff, 0x60, 0x30, 0x18, 0x0c, 0x06, 0xff, 0x00, 0x00, 0x00, 0x00},
{0x00, 0x00, 0x0f, 0x18, 0x18, 0x18, 0x38, 0xf0, 0x38, 0x18, 0x18, 0x18, 0x0f},
{0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18},
{0x00, 0x00, 0xf0, 0x18, 0x18, 0x18, 0x1c, 0x0f, 0x1c, 0x18, 0x18, 0x18, 0xf0},
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x8f, 0xf1, 0x60, 0x00, 0x00, 0x00}
};
static const char *s_vertexShaderSrc =
"uniform vec2 charSize;\n"
"ATTRIN vec2 rawpos;\n"
"ATTRIN vec2 tex0;\n"
"VARYOUT vec2 uv0;\n"
"void main(void) {\n"
" gl_Position = vec4(rawpos,0,1);\n"
" uv0 = tex0 * charSize;\n"
"}\n";
static const char *s_fragmentShaderSrc =
"uniform sampler2D samp8;\n"
"uniform vec4 color;\n"
"VARYIN vec2 uv0;\n"
"COLOROUT(ocol0)\n"
"void main(void) {\n"
" ocol0 = texture(samp8,uv0) * color;\n"
"}\n";
static SHADER s_shader;
RasterFont::RasterFont()
{
// generate the texture
glGenTextures(1, &texture);
glActiveTexture(GL_TEXTURE0+8);
glBindTexture(GL_TEXTURE_2D, texture);
u32* texture_data = new u32[char_width*char_count*char_height];
for(u32 y=0; y<char_height; y++) {
for(u32 c=0; c<char_count; c++) {
for(u32 x=0; x<char_width; x++) {
bool pixel = rasters[c][y] & (1<<(char_width-x-1));
texture_data[char_width*char_count*y+char_width*c+x] = pixel ? -1 : 0;
}
}
}
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, char_width*char_count, char_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, texture_data);
delete [] texture_data;
// generate shader
ProgramShaderCache::CompileShader(s_shader, s_vertexShaderSrc, s_fragmentShaderSrc);
// bound uniforms
glUniform2f(glGetUniformLocation(s_shader.glprogid,"charSize"), 1.0f / GLfloat(char_count), 1.0f);
uniform_color_id = glGetUniformLocation(s_shader.glprogid,"color");
glUniform4f(uniform_color_id, 1.0f, 1.0f, 1.0f, 1.0f);
cached_color = -1;
// generate VBO & VAO
glGenBuffers(1, &VBO);
glGenVertexArrays(1, &VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBindVertexArray(VAO);
glEnableVertexAttribArray(SHADER_POSITION_ATTRIB);
glVertexAttribPointer(SHADER_POSITION_ATTRIB, 2, GL_FLOAT, 0, sizeof(GLfloat)*4, NULL);
glEnableVertexAttribArray(SHADER_TEXTURE0_ATTRIB);
glVertexAttribPointer(SHADER_TEXTURE0_ATTRIB, 2, GL_FLOAT, 0, sizeof(GLfloat)*4, (GLfloat*)NULL+2);
}
RasterFont::~RasterFont()
{
glDeleteTextures(1, &texture);
glDeleteBuffers(1, &VBO);
glDeleteVertexArrays(1, &VAO);
s_shader.Destroy();
}
void RasterFont::printMultilineText(const char *text, double start_x, double start_y, double z, int bbWidth, int bbHeight, u32 color)
{
size_t length = strlen(text);
GLfloat *vertices = new GLfloat[length*6*4];
int usage = 0;
GLfloat delta_x = GLfloat(2*char_width)/GLfloat(bbWidth);
GLfloat delta_y = GLfloat(2*char_height)/GLfloat(bbHeight);
GLfloat border_x = 2.0f/GLfloat(bbWidth);
GLfloat border_y = 4.0f/GLfloat(bbHeight);
GLfloat x = GLfloat(start_x);
GLfloat y = GLfloat(start_y);
for(size_t i=0; i<length; i++) {
u8 c = text[i];
if(c == '\n') {
x = GLfloat(start_x);
y -= delta_y + border_y;
continue;
}
// do not print spaces, they can be skipped easily
if(c == ' ') {
x += delta_x + border_x;
continue;
}
if(c < char_offset || c >= char_count+char_offset) continue;
vertices[usage++] = x;
vertices[usage++] = y;
vertices[usage++] = GLfloat(c-char_offset);
vertices[usage++] = 0.0f;
vertices[usage++] = x+delta_x;
vertices[usage++] = y;
vertices[usage++] = GLfloat(c-char_offset+1);
vertices[usage++] = 0.0f;
vertices[usage++] = x+delta_x;
vertices[usage++] = y+delta_y;
vertices[usage++] = GLfloat(c-char_offset+1);
vertices[usage++] = 1.0f;
vertices[usage++] = x;
vertices[usage++] = y;
vertices[usage++] = GLfloat(c-char_offset);
vertices[usage++] = 0.0f;
vertices[usage++] = x+delta_x;
vertices[usage++] = y+delta_y;
vertices[usage++] = GLfloat(c-char_offset+1);
vertices[usage++] = 1.0f;
vertices[usage++] = x;
vertices[usage++] = y+delta_y;
vertices[usage++] = GLfloat(c-char_offset);
vertices[usage++] = 1.0f;
x += delta_x + border_x;
}
if(!usage) {
delete [] vertices;
return;
}
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, usage*sizeof(GLfloat), vertices, GL_STREAM_DRAW);
delete [] vertices;
s_shader.Bind();
if(color != cached_color) {
glUniform4f(uniform_color_id, GLfloat((color>>16)&0xff)/255.f,GLfloat((color>>8)&0xff)/255.f,GLfloat((color>>0)&0xff)/255.f,GLfloat((color>>24)&0xff)/255.f);
cached_color = color;
}
glActiveTexture(GL_TEXTURE0+8);
glDrawArrays(GL_TRIANGLES, 0, usage/4);
}
}

View File

@ -0,0 +1,28 @@
// Copyright 2013 Dolphin Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.
#ifndef _RASTERFONT_H_
#define _RASTERFONT_H_
namespace OGL {
class RasterFont {
public:
RasterFont();
~RasterFont(void);
static int debug;
void printMultilineText(const char *text, double x, double y, double z, int bbWidth, int bbHeight, u32 color);
private:
u32 VBO;
u32 VAO;
u32 texture;
u32 uniform_color_id;
u32 cached_color;
};
}
#endif // _RASTERFONT_H_

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,99 @@
#ifndef _RENDER_H_
#define _RENDER_H_
#include "RenderBase.h"
namespace OGL
{
void ClearEFBCache();
enum GLSL_VERSION {
GLSL_130,
GLSL_140,
GLSL_150, // and above
GLSLES2,
GLSLES3
};
// ogl-only config, so not in VideoConfig.h
extern struct VideoConfig {
bool bSupportsGLSLCache;
bool bSupportsGLPinnedMemory;
bool bSupportsGLSync;
bool bSupportsGLBaseVertex;
bool bSupportCoverageMSAA;
bool bSupportSampleShading;
GLSL_VERSION eSupportedGLSLVersion;
bool bSupportOGL31;
const char *gl_vendor;
const char *gl_renderer;
const char* gl_version;
const char* glsl_version;
s32 max_samples;
} g_ogl_config;
class Renderer : public ::Renderer
{
public:
Renderer();
~Renderer();
static void Init();
static void Shutdown();
void SetColorMask();
void SetBlendMode(bool forceUpdate);
void SetScissorRect(const TargetRectangle& rc);
void SetGenerationMode();
void SetDepthMode();
void SetLogicOpMode();
void SetDitherMode();
void SetLineWidth();
void SetSamplerState(int stage,int texindex);
void SetInterlacingMode();
// TODO: Implement and use these
void ApplyState(bool bUseDstAlpha) {}
void RestoreState() {}
void RenderText(const char* pstr, int left, int top, u32 color);
void DrawDebugInfo();
void FlipImageData(u8 *data, int w, int h);
u32 AccessEFB(EFBAccessType type, u32 x, u32 y, u32 poke_data);
void ResetAPIState();
void RestoreAPIState();
TargetRectangle ConvertEFBRectangle(const EFBRectangle& rc);
void Swap(u32 xfbAddr, u32 fbWidth, u32 fbHeight, const EFBRectangle& rc,float Gamma);
void ClearScreen(const EFBRectangle& rc, bool colorEnable, bool alphaEnable, bool zEnable, u32 color, u32 z);
void ReinterpretPixelData(unsigned int convtype);
void UpdateViewport(Matrix44& vpCorrection);
bool SaveScreenshot(const std::string &filename, const TargetRectangle &rc);
void SetPSConstant4f(unsigned int const_number, float f1, float f2, float f3, float f4);
void SetPSConstant4fv(unsigned int const_number, const float *f);
void SetMultiPSConstant4fv(unsigned int const_number, unsigned int count, const float *f);
void SetVSConstant4f(unsigned int const_number, float f1, float f2, float f3, float f4);
void SetVSConstant4fv(unsigned int const_number, const float *f);
void SetMultiVSConstant3fv(unsigned int const_number, unsigned int count, const float *f);
void SetMultiVSConstant4fv(unsigned int const_number, unsigned int count, const float *f);
private:
void UpdateEFBCache(EFBAccessType type, u32 cacheRectIdx, const EFBRectangle& efbPixelRc, const TargetRectangle& targetPixelRc, const u32* data);
};
}
#endif

View File

@ -0,0 +1,122 @@
// Copyright 2013 Dolphin Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.
#include "DriverDetails.h"
#include "SamplerCache.h"
namespace OGL
{
SamplerCache *g_sampler_cache;
SamplerCache::SamplerCache()
: m_last_max_anisotropy()
{}
SamplerCache::~SamplerCache()
{
if (!DriverDetails::HasBug(DriverDetails::BUG_ISTEGRA))
Clear();
}
void SamplerCache::SetSamplerState(int stage, const TexMode0& tm0, const TexMode1& tm1)
{
if (DriverDetails::HasBug(DriverDetails::BUG_ISTEGRA))
return;
// TODO: can this go somewhere else?
if (m_last_max_anisotropy != g_ActiveConfig.iMaxAnisotropy)
{
m_last_max_anisotropy = g_ActiveConfig.iMaxAnisotropy;
Clear();
}
Params params(tm0, tm1);
// take equivalent forced linear when bForceFiltering
if (g_ActiveConfig.bForceFiltering)
{
params.tm0.min_filter |= 0x4;
params.tm0.mag_filter |= 0x1;
}
// TODO: Should keep a circular buffer for each stage of recently used samplers.
auto& active_sampler = m_active_samplers[stage];
if (active_sampler.first != params || !active_sampler.second.sampler_id)
{
// Active sampler does not match parameters (or is invalid), bind the proper one.
active_sampler.first = params;
active_sampler.second = GetEntry(params);
glBindSampler(stage, active_sampler.second.sampler_id);
}
}
auto SamplerCache::GetEntry(const Params& params) -> Value&
{
auto& val = m_cache[params];
if (!val.sampler_id)
{
// Sampler not found in cache, create it.
glGenSamplers(1, &val.sampler_id);
SetParameters(val.sampler_id, params);
// TODO: Maybe kill old samplers if the cache gets huge. It doesn't seem to get huge though.
//ERROR_LOG(VIDEO, "Sampler cache size is now %ld.", m_cache.size());
}
return val;
}
void SamplerCache::SetParameters(GLuint sampler_id, const Params& params)
{
static const GLint min_filters[8] =
{
GL_NEAREST,
GL_NEAREST_MIPMAP_NEAREST,
GL_NEAREST_MIPMAP_LINEAR,
GL_NEAREST,
GL_LINEAR,
GL_LINEAR_MIPMAP_NEAREST,
GL_LINEAR_MIPMAP_LINEAR,
GL_LINEAR,
};
static const GLint wrap_settings[4] =
{
GL_CLAMP_TO_EDGE,
GL_REPEAT,
GL_MIRRORED_REPEAT,
GL_REPEAT,
};
auto& tm0 = params.tm0;
auto& tm1 = params.tm1;
glSamplerParameteri(sampler_id, GL_TEXTURE_MIN_FILTER, min_filters[tm0.min_filter % ArraySize(min_filters)]);
glSamplerParameteri(sampler_id, GL_TEXTURE_MAG_FILTER, tm0.mag_filter ? GL_LINEAR : GL_NEAREST);
glSamplerParameteri(sampler_id, GL_TEXTURE_WRAP_S, wrap_settings[tm0.wrap_s]);
glSamplerParameteri(sampler_id, GL_TEXTURE_WRAP_T, wrap_settings[tm0.wrap_t]);
glSamplerParameterf(sampler_id, GL_TEXTURE_MIN_LOD, tm1.min_lod / 16.f);
glSamplerParameterf(sampler_id, GL_TEXTURE_MAX_LOD, tm1.max_lod / 16.f);
#ifndef USE_GLES3
glSamplerParameterf(sampler_id, GL_TEXTURE_LOD_BIAS, (s32)tm0.lod_bias / 32.f);
if (g_ActiveConfig.iMaxAnisotropy > 0)
glSamplerParameterf(sampler_id, GL_TEXTURE_MAX_ANISOTROPY_EXT, (float)(1 << g_ActiveConfig.iMaxAnisotropy));
#endif
}
void SamplerCache::Clear()
{
for (auto it = m_cache.begin(); it != m_cache.end(); ++it)
{
glDeleteSamplers(1, &it->second.sampler_id);
}
m_cache.clear();
}
}

View File

@ -0,0 +1,80 @@
#ifndef INCLUDE_SAMPLER_CACHE_H_
#define INCLUDE_SAMPLER_CACHE_H_
#include <map>
#include "Render.h"
#include "GLUtil.h"
namespace OGL
{
class SamplerCache : NonCopyable
{
public:
SamplerCache();
~SamplerCache();
void SetSamplerState(int stage, const TexMode0& tm0, const TexMode1& tm1);
void Clear();
private:
struct Params
{
union
{
struct
{
TexMode0 tm0;
TexMode1 tm1;
};
u64 hex;
};
Params()
: hex()
{}
Params(const TexMode0& _tm0, const TexMode1& _tm1)
: tm0(_tm0)
, tm1(_tm1)
{
static_assert(sizeof(Params) == 8, "Assuming I can treat this as a 64bit int.");
}
bool operator<(const Params& other) const
{
return hex < other.hex;
}
bool operator!=(const Params& other) const
{
return hex != other.hex;
}
};
struct Value
{
Value()
: sampler_id()
{}
GLuint sampler_id;
};
void SetParameters(GLuint sampler_id, const Params& params);
Value& GetEntry(const Params& params);
std::map<Params, Value> m_cache;
std::pair<Params, Value> m_active_samplers[8];
int m_last_max_anisotropy;
};
extern SamplerCache *g_sampler_cache;
}
#endif

View File

@ -0,0 +1,255 @@
// Copyright 2013 Dolphin Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.
#include "Globals.h"
#include "GLUtil.h"
#include "StreamBuffer.h"
#include "MemoryUtil.h"
#include "Render.h"
#include "DriverDetails.h"
#include "OnScreenDisplay.h"
namespace OGL
{
static const u32 SYNC_POINTS = 16;
static const u32 ALIGN_PINNED_MEMORY = 4096;
StreamBuffer::StreamBuffer(u32 type, size_t size, StreamType uploadType)
: m_uploadtype(uploadType), m_buffertype(type), m_size(size)
{
glGenBuffers(1, &m_buffer);
bool nvidia = !strcmp(g_ogl_config.gl_vendor, "NVIDIA Corporation");
if(m_uploadtype & STREAM_DETECT)
{
// TODO: move this to InitBackendInfo
if(g_ActiveConfig.bHackedBufferUpload && DriverDetails::HasBug(DriverDetails::BUG_BROKENHACKEDBUFFER))
{
OSD::AddMessage("Vertex Streaming Hack isn't supported by your GPU.", 10000);
g_ActiveConfig.bHackedBufferUpload = false;
g_Config.bHackedBufferUpload = false;
}
if(!g_ogl_config.bSupportsGLBaseVertex && (m_uploadtype & BUFFERDATA)
&& !DriverDetails::HasBug(DriverDetails::BUG_ISPOWERVR)
&& !DriverDetails::HasBug(DriverDetails::BUG_ISTEGRA))
m_uploadtype = BUFFERDATA;
else if(!g_ogl_config.bSupportsGLBaseVertex && (m_uploadtype & BUFFERSUBDATA))
m_uploadtype = BUFFERSUBDATA;
else if(g_ogl_config.bSupportsGLSync && g_ActiveConfig.bHackedBufferUpload && (m_uploadtype & MAP_AND_RISK))
m_uploadtype = MAP_AND_RISK;
else if(g_ogl_config.bSupportsGLSync && g_ogl_config.bSupportsGLPinnedMemory && (!DriverDetails::HasBug(DriverDetails::BUG_BROKENPINNEDMEMORY) || type != GL_ELEMENT_ARRAY_BUFFER) && (m_uploadtype & PINNED_MEMORY))
m_uploadtype = PINNED_MEMORY;
else if(nvidia && (m_uploadtype & BUFFERSUBDATA))
m_uploadtype = BUFFERSUBDATA;
else if(g_ogl_config.bSupportsGLSync && (m_uploadtype & MAP_AND_SYNC))
m_uploadtype = MAP_AND_SYNC;
else
m_uploadtype = MAP_AND_ORPHAN;
}
Init();
}
StreamBuffer::~StreamBuffer()
{
Shutdown();
glDeleteBuffers(1, &m_buffer);
}
#define SLOT(x) (x)*SYNC_POINTS/m_size
void StreamBuffer::Alloc ( size_t size, u32 stride )
{
size_t m_iterator_aligned = m_iterator;
if(m_iterator_aligned && stride) {
m_iterator_aligned--;
m_iterator_aligned = m_iterator_aligned - (m_iterator_aligned % stride) + stride;
}
size_t iter_end = m_iterator_aligned + size;
switch(m_uploadtype) {
case MAP_AND_ORPHAN:
if(iter_end >= m_size) {
glBufferData(m_buffertype, m_size, NULL, GL_STREAM_DRAW);
m_iterator_aligned = 0;
}
break;
case MAP_AND_SYNC:
case PINNED_MEMORY:
// insert waiting slots for used memory
for(u32 i=SLOT(m_used_iterator); i<SLOT(m_iterator); i++)
{
fences[i] = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
}
m_used_iterator = m_iterator;
// wait for new slots to end of buffer
for(u32 i=SLOT(m_free_iterator)+1; i<=SLOT(iter_end) && i < SYNC_POINTS; i++)
{
glClientWaitSync(fences[i], GL_SYNC_FLUSH_COMMANDS_BIT, GL_TIMEOUT_IGNORED);
glDeleteSync(fences[i]);
}
m_free_iterator = iter_end;
// if buffer is full
if(iter_end >= m_size) {
// insert waiting slots in unused space at the end of the buffer
for(u32 i=SLOT(m_used_iterator); i < SYNC_POINTS; i++)
fences[i] = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
// move to the start
m_used_iterator = m_iterator_aligned = m_iterator = 0; // offset 0 is always aligned
iter_end = size;
// wait for space at the start
for(u32 i=0; i<=SLOT(iter_end); i++)
{
glClientWaitSync(fences[i], GL_SYNC_FLUSH_COMMANDS_BIT, GL_TIMEOUT_IGNORED);
glDeleteSync(fences[i]);
}
m_free_iterator = iter_end;
}
break;
case MAP_AND_RISK:
if(iter_end >= m_size) {
m_iterator_aligned = 0;
}
break;
case BUFFERSUBDATA:
case BUFFERDATA:
m_iterator_aligned = 0;
break;
case STREAM_DETECT:
case DETECT_MASK: // Just to shutup warnings
break;
}
m_iterator = m_iterator_aligned;
}
size_t StreamBuffer::Upload ( u8* data, size_t size )
{
switch(m_uploadtype) {
case MAP_AND_SYNC:
case MAP_AND_ORPHAN:
pointer = (u8*)glMapBufferRange(m_buffertype, m_iterator, size, GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT);
if(pointer) {
memcpy(pointer, data, size);
glUnmapBuffer(m_buffertype);
} else {
ERROR_LOG(VIDEO, "Buffer mapping failed");
}
break;
case PINNED_MEMORY:
case MAP_AND_RISK:
if(pointer)
memcpy(pointer+m_iterator, data, size);
break;
case BUFFERSUBDATA:
glBufferSubData(m_buffertype, m_iterator, size, data);
break;
case BUFFERDATA:
glBufferData(m_buffertype, size, data, GL_STREAM_DRAW);
break;
case STREAM_DETECT:
case DETECT_MASK: // Just to shutup warnings
break;
}
size_t ret = m_iterator;
m_iterator += size;
return ret;
}
void StreamBuffer::Init()
{
m_iterator = 0;
m_used_iterator = 0;
m_free_iterator = 0;
switch(m_uploadtype) {
case MAP_AND_SYNC:
fences = new GLsync[SYNC_POINTS];
for(u32 i=0; i<SYNC_POINTS; i++)
fences[i] = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
case MAP_AND_ORPHAN:
case BUFFERSUBDATA:
glBindBuffer(m_buffertype, m_buffer);
glBufferData(m_buffertype, m_size, NULL, GL_STREAM_DRAW);
break;
case PINNED_MEMORY:
glGetError(); // errors before this allocation should be ignored
fences = new GLsync[SYNC_POINTS];
for(u32 i=0; i<SYNC_POINTS; i++)
fences[i] = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
pointer = (u8*)AllocateAlignedMemory(ROUND_UP(m_size,ALIGN_PINNED_MEMORY), ALIGN_PINNED_MEMORY );
glBindBuffer(GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD, m_buffer);
glBufferData(GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD, ROUND_UP(m_size,ALIGN_PINNED_MEMORY), pointer, GL_STREAM_COPY);
glBindBuffer(GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD, 0);
glBindBuffer(m_buffertype, m_buffer);
// on error, switch to another backend. some old catalyst seems to have broken pinned memory support
if(glGetError() != GL_NO_ERROR) {
ERROR_LOG(VIDEO, "Pinned memory detected, but not working. Please report this: %s, %s, %s", g_ogl_config.gl_vendor, g_ogl_config.gl_renderer, g_ogl_config.gl_version);
Shutdown();
m_uploadtype = MAP_AND_SYNC;
Init();
}
break;
case MAP_AND_RISK:
glBindBuffer(m_buffertype, m_buffer);
glBufferData(m_buffertype, m_size, NULL, GL_STREAM_DRAW);
pointer = (u8*)glMapBufferRange(m_buffertype, 0, m_size, GL_MAP_WRITE_BIT);
glUnmapBuffer(m_buffertype);
if(!pointer)
ERROR_LOG(VIDEO, "Buffer allocation failed");
break;
case BUFFERDATA:
glBindBuffer(m_buffertype, m_buffer);
break;
case STREAM_DETECT:
case DETECT_MASK: // Just to shutup warnings
break;
}
}
void StreamBuffer::Shutdown()
{
switch(m_uploadtype) {
case MAP_AND_SYNC:
DeleteFences();
break;
case MAP_AND_RISK:
case MAP_AND_ORPHAN:
case BUFFERSUBDATA:
case BUFFERDATA:
break;
case PINNED_MEMORY:
DeleteFences();
glBindBuffer(m_buffertype, 0);
glFinish(); // ogl pipeline must be flushed, else this buffer can be in use
FreeAlignedMemory(pointer);
break;
case STREAM_DETECT:
case DETECT_MASK: // Just to shutup warnings
break;
}
}
void StreamBuffer::DeleteFences()
{
for(u32 i=SLOT(m_free_iterator)+1; i < SYNC_POINTS; i++)
glDeleteSync(fences[i]);
for(u32 i=0; i<SLOT(m_iterator); i++)
glDeleteSync(fences[i]);
delete [] fences;
}
}

View File

@ -0,0 +1,60 @@
// Copyright 2013 Dolphin Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.
#ifndef STREAMBUFFER_H
#define STREAMBUFFER_H
#include "VideoCommon.h"
#include "FramebufferManager.h"
#include "GLUtil.h"
// glew < 1.8 doesn't support pinned memory
#ifndef GLEW_AMD_pinned_memory
#define GLEW_AMD_pinned_memory glewIsSupported("GL_AMD_pinned_memory")
#define GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD 0x9160
#endif
namespace OGL
{
enum StreamType {
DETECT_MASK = 0x7F,
STREAM_DETECT = (1 << 0),
MAP_AND_ORPHAN = (1 << 1),
MAP_AND_SYNC = (1 << 2),
MAP_AND_RISK = (1 << 3),
PINNED_MEMORY = (1 << 4),
BUFFERSUBDATA = (1 << 5),
BUFFERDATA = (1 << 6)
};
class StreamBuffer {
public:
StreamBuffer(u32 type, size_t size, StreamType uploadType = DETECT_MASK);
~StreamBuffer();
void Alloc(size_t size, u32 stride = 0);
size_t Upload(u8 *data, size_t size);
u32 getBuffer() { return m_buffer; }
private:
void Init();
void Shutdown();
void DeleteFences();
StreamType m_uploadtype;
u32 m_buffer;
u32 m_buffertype;
size_t m_size;
u8 *pointer;
size_t m_iterator;
size_t m_used_iterator;
size_t m_free_iterator;
GLsync *fences;
};
}
#endif // STREAMBUFFER_H

View File

@ -0,0 +1,490 @@
// Copyright 2013 Dolphin Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.
#include <vector>
#include <cmath>
#include <fstream>
#ifdef _WIN32
#define _interlockedbittestandset workaround_ms_header_bug_platform_sdk6_set
#define _interlockedbittestandreset workaround_ms_header_bug_platform_sdk6_reset
#define _interlockedbittestandset64 workaround_ms_header_bug_platform_sdk6_set64
#define _interlockedbittestandreset64 workaround_ms_header_bug_platform_sdk6_reset64
#include <intrin.h>
#undef _interlockedbittestandset
#undef _interlockedbittestandreset
#undef _interlockedbittestandset64
#undef _interlockedbittestandreset64
#endif
#include "BPStructs.h"
#include "CommonPaths.h"
#include "FileUtil.h"
#include "FramebufferManager.h"
#include "Globals.h"
#include "Hash.h"
#include "HiresTextures.h"
#include "HW/Memmap.h"
#include "ImageWrite.h"
#include "MemoryUtil.h"
#include "ProgramShaderCache.h"
#include "PixelShaderManager.h"
#include "Render.h"
#include "Statistics.h"
#include "StringUtil.h"
#include "TextureCache.h"
#include "TextureConverter.h"
#include "TextureDecoder.h"
#include "VertexShaderManager.h"
#include "VideoConfig.h"
namespace OGL
{
static SHADER s_ColorMatrixProgram;
static SHADER s_DepthMatrixProgram;
static GLuint s_ColorMatrixUniform;
static GLuint s_DepthMatrixUniform;
static u32 s_ColorCbufid;
static u32 s_DepthCbufid;
static u32 s_Textures[8];
static u32 s_ActiveTexture;
static u32 s_NextStage;
struct VBOCache {
GLuint vbo;
GLuint vao;
TargetRectangle targetSource;
};
static std::map<u64,VBOCache> s_VBO;
bool SaveTexture(const char* filename, u32 textarget, u32 tex, int virtual_width, int virtual_height, unsigned int level)
{
#ifndef USE_GLES3
int width = std::max(virtual_width >> level, 1);
int height = std::max(virtual_height >> level, 1);
std::vector<u32> data(width * height);
glActiveTexture(GL_TEXTURE0+9);
glBindTexture(textarget, tex);
glGetTexImage(textarget, level, GL_BGRA, GL_UNSIGNED_BYTE, &data[0]);
glBindTexture(textarget, 0);
TextureCache::SetStage();
const GLenum err = GL_REPORT_ERROR();
if (GL_NO_ERROR != err)
{
PanicAlert("Can't save texture, GL Error: %s", gluErrorString(err));
return false;
}
return SaveTGA(filename, width, height, &data[0]);
#else
return false;
#endif
}
TextureCache::TCacheEntry::~TCacheEntry()
{
if (texture)
{
for(int i=0; i<8; i++)
if(s_Textures[i] == texture)
s_Textures[i] = 0;
glDeleteTextures(1, &texture);
texture = 0;
}
if (framebuffer)
{
glDeleteFramebuffers(1, &framebuffer);
framebuffer = 0;
}
}
TextureCache::TCacheEntry::TCacheEntry()
{
glGenTextures(1, &texture);
GL_REPORT_ERRORD();
framebuffer = 0;
}
void TextureCache::TCacheEntry::Bind(unsigned int stage)
{
if (s_Textures[stage] != texture)
{
if (s_ActiveTexture != stage)
{
glActiveTexture(GL_TEXTURE0 + stage);
s_ActiveTexture = stage;
}
glBindTexture(GL_TEXTURE_2D, texture);
s_Textures[stage] = texture;
}
}
bool TextureCache::TCacheEntry::Save(const char filename[], unsigned int level)
{
// TODO: make ogl dump PNGs
std::string tga_filename(filename);
tga_filename.replace(tga_filename.size() - 3, 3, "tga");
return SaveTexture(tga_filename.c_str(), GL_TEXTURE_2D, texture, virtual_width, virtual_height, level);
}
TextureCache::TCacheEntryBase* TextureCache::CreateTexture(unsigned int width,
unsigned int height, unsigned int expanded_width,
unsigned int tex_levels, PC_TexFormat pcfmt)
{
int gl_format = 0,
gl_iformat = 0,
gl_type = 0;
if (pcfmt != PC_TEX_FMT_DXT1)
{
switch (pcfmt)
{
default:
case PC_TEX_FMT_NONE:
PanicAlert("Invalid PC texture format %i", pcfmt);
case PC_TEX_FMT_BGRA32:
gl_format = GL_BGRA;
gl_iformat = GL_RGBA;
gl_type = GL_UNSIGNED_BYTE;
break;
case PC_TEX_FMT_RGBA32:
gl_format = GL_RGBA;
gl_iformat = GL_RGBA;
gl_type = GL_UNSIGNED_BYTE;
break;
#ifndef USE_GLES3
case PC_TEX_FMT_I4_AS_I8:
gl_format = GL_LUMINANCE;
gl_iformat = GL_INTENSITY4;
gl_type = GL_UNSIGNED_BYTE;
break;
case PC_TEX_FMT_IA4_AS_IA8:
gl_format = GL_LUMINANCE_ALPHA;
gl_iformat = GL_LUMINANCE4_ALPHA4;
gl_type = GL_UNSIGNED_BYTE;
break;
case PC_TEX_FMT_I8:
gl_format = GL_LUMINANCE;
gl_iformat = GL_INTENSITY8;
gl_type = GL_UNSIGNED_BYTE;
break;
case PC_TEX_FMT_IA8:
gl_format = GL_LUMINANCE_ALPHA;
gl_iformat = GL_LUMINANCE8_ALPHA8;
gl_type = GL_UNSIGNED_BYTE;
break;
#endif
case PC_TEX_FMT_RGB565:
gl_format = GL_RGB;
gl_iformat = GL_RGB;
gl_type = GL_UNSIGNED_SHORT_5_6_5;
break;
}
}
TCacheEntry &entry = *new TCacheEntry;
entry.gl_format = gl_format;
entry.gl_iformat = gl_iformat;
entry.gl_type = gl_type;
entry.pcfmt = pcfmt;
entry.m_tex_levels = tex_levels;
entry.Load(width, height, expanded_width, 0);
return &entry;
}
void TextureCache::TCacheEntry::Load(unsigned int width, unsigned int height,
unsigned int expanded_width, unsigned int level)
{
if (s_ActiveTexture != s_NextStage)
{
glActiveTexture(GL_TEXTURE0 + s_NextStage);
s_ActiveTexture = s_NextStage;
}
if (s_Textures[s_NextStage] != texture)
{
glBindTexture(GL_TEXTURE_2D, texture);
s_Textures[s_NextStage] = texture;
}
// TODO: sloppy, just do this on creation?
if (level == 0)
{
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, m_tex_levels - 1);
}
if (pcfmt != PC_TEX_FMT_DXT1)
{
if (expanded_width != width)
glPixelStorei(GL_UNPACK_ROW_LENGTH, expanded_width);
glTexImage2D(GL_TEXTURE_2D, level, gl_iformat, width, height, 0, gl_format, gl_type, temp);
if (expanded_width != width)
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
}
else
{
PanicAlert("PC_TEX_FMT_DXT1 support disabled");
//glCompressedTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT,
//width, height, 0, expanded_width * expanded_height/2, temp);
}
GL_REPORT_ERRORD();
}
TextureCache::TCacheEntryBase* TextureCache::CreateRenderTargetTexture(
unsigned int scaled_tex_w, unsigned int scaled_tex_h)
{
TCacheEntry *const entry = new TCacheEntry;
glActiveTexture(GL_TEXTURE0+9);
glBindTexture(GL_TEXTURE_2D, entry->texture);
GL_REPORT_ERRORD();
const GLenum
gl_format = GL_RGBA,
gl_iformat = GL_RGBA,
gl_type = GL_UNSIGNED_BYTE;
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
entry->m_tex_levels = 1;
glTexImage2D(GL_TEXTURE_2D, 0, gl_iformat, scaled_tex_w, scaled_tex_h, 0, gl_format, gl_type, NULL);
glBindTexture(GL_TEXTURE_2D, 0);
glGenFramebuffers(1, &entry->framebuffer);
FramebufferManager::SetFramebuffer(entry->framebuffer);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, entry->texture, 0);
GL_REPORT_FBO_ERROR();
SetStage();
GL_REPORT_ERRORD();
return entry;
}
void TextureCache::TCacheEntry::FromRenderTarget(u32 dstAddr, unsigned int dstFormat,
unsigned int srcFormat, const EFBRectangle& srcRect,
bool isIntensity, bool scaleByHalf, unsigned int cbufid,
const float *colmat)
{
g_renderer->ResetAPIState(); // reset any game specific settings
// Make sure to resolve anything we need to read from.
const GLuint read_texture = (srcFormat == PIXELFMT_Z24) ?
FramebufferManager::ResolveAndGetDepthTarget(srcRect) :
FramebufferManager::ResolveAndGetRenderTarget(srcRect);
GL_REPORT_ERRORD();
if (type != TCET_EC_DYNAMIC || g_ActiveConfig.bCopyEFBToTexture)
{
FramebufferManager::SetFramebuffer(framebuffer);
GL_REPORT_ERRORD();
glActiveTexture(GL_TEXTURE0+9);
glBindTexture(getFbType(), read_texture);
glViewport(0, 0, virtual_width, virtual_height);
if(srcFormat == PIXELFMT_Z24) {
s_DepthMatrixProgram.Bind();
if(s_DepthCbufid != cbufid)
glUniform4fv(s_DepthMatrixUniform, 5, colmat);
s_DepthCbufid = cbufid;
} else {
s_ColorMatrixProgram.Bind();
if(s_ColorCbufid != cbufid)
glUniform4fv(s_ColorMatrixUniform, 7, colmat);
s_ColorCbufid = cbufid;
}
GL_REPORT_ERRORD();
TargetRectangle targetSource = g_renderer->ConvertEFBRectangle(srcRect);
GL_REPORT_ERRORD();
// should be unique enough, if not, vbo will "only" be uploaded to much
u64 targetSourceHash = u64(targetSource.left)<<48 | u64(targetSource.top)<<32 | u64(targetSource.right)<<16 | u64(targetSource.bottom);
std::map<u64, VBOCache>::iterator vbo_it = s_VBO.find(targetSourceHash);
if(vbo_it == s_VBO.end()) {
VBOCache item;
item.targetSource.bottom = -1;
item.targetSource.top = -1;
item.targetSource.left = -1;
item.targetSource.right = -1;
glGenBuffers(1, &item.vbo);
glGenVertexArrays(1, &item.vao);
glBindBuffer(GL_ARRAY_BUFFER, item.vbo);
glBindVertexArray(item.vao);
glEnableVertexAttribArray(SHADER_POSITION_ATTRIB);
glVertexAttribPointer(SHADER_POSITION_ATTRIB, 2, GL_FLOAT, 0, sizeof(GLfloat)*4, (GLfloat*)NULL);
glEnableVertexAttribArray(SHADER_TEXTURE0_ATTRIB);
glVertexAttribPointer(SHADER_TEXTURE0_ATTRIB, 2, GL_FLOAT, 0, sizeof(GLfloat)*4, (GLfloat*)NULL+2);
vbo_it = s_VBO.insert(std::pair<u64,VBOCache>(targetSourceHash, item)).first;
}
if(!(vbo_it->second.targetSource == targetSource)) {
GLfloat vertices[] = {
-1.f, 1.f,
(GLfloat)targetSource.left, (GLfloat)targetSource.bottom,
-1.f, -1.f,
(GLfloat)targetSource.left, (GLfloat)targetSource.top,
1.f, 1.f,
(GLfloat)targetSource.right, (GLfloat)targetSource.bottom,
1.f, -1.f,
(GLfloat)targetSource.right, (GLfloat)targetSource.top
};
glBindBuffer(GL_ARRAY_BUFFER, vbo_it->second.vbo);
glBufferData(GL_ARRAY_BUFFER, 4*4*sizeof(GLfloat), vertices, GL_STREAM_DRAW);
vbo_it->second.targetSource = targetSource;
}
glBindVertexArray(vbo_it->second.vao);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
GL_REPORT_ERRORD();
}
if (false == g_ActiveConfig.bCopyEFBToTexture)
{
int encoded_size = TextureConverter::EncodeToRamFromTexture(
addr,
read_texture,
srcFormat == PIXELFMT_Z24,
isIntensity,
dstFormat,
scaleByHalf,
srcRect);
u8* dst = Memory::GetPointer(addr);
u64 const new_hash = GetHash64(dst,encoded_size,g_ActiveConfig.iSafeTextureCache_ColorSamples);
// Mark texture entries in destination address range dynamic unless caching is enabled and the texture entry is up to date
if (!g_ActiveConfig.bEFBCopyCacheEnable)
TextureCache::MakeRangeDynamic(addr,encoded_size);
else if (!TextureCache::Find(addr, new_hash))
TextureCache::MakeRangeDynamic(addr,encoded_size);
hash = new_hash;
}
FramebufferManager::SetFramebuffer(0);
VertexShaderManager::SetViewportChanged();
GL_REPORT_ERRORD();
if (g_ActiveConfig.bDumpEFBTarget)
{
static int count = 0;
SaveTexture(StringFromFormat("%sefb_frame_%i.tga", File::GetUserPath(D_DUMPTEXTURES_IDX).c_str(),
count++).c_str(), GL_TEXTURE_2D, texture, virtual_width, virtual_height, 0);
}
g_renderer->RestoreAPIState();
}
TextureCache::TextureCache()
{
const char *pColorMatrixProg =
"uniform sampler2DRect samp9;\n"
"uniform vec4 colmat[7];\n"
"VARYIN vec2 uv0;\n"
"COLOROUT(ocol0)\n"
"\n"
"void main(){\n"
" vec4 texcol = texture2DRect(samp9, uv0);\n"
" texcol = round(texcol * colmat[5]) * colmat[6];\n"
" ocol0 = texcol * mat4(colmat[0], colmat[1], colmat[2], colmat[3]) + colmat[4];\n"
"}\n";
const char *pDepthMatrixProg =
"uniform sampler2DRect samp9;\n"
"uniform vec4 colmat[5];\n"
"VARYIN vec2 uv0;\n"
"COLOROUT(ocol0)\n"
"\n"
"void main(){\n"
" vec4 texcol = texture2DRect(samp9, uv0);\n"
" vec4 EncodedDepth = fract((texcol.r * (16777215.0/16777216.0)) * vec4(1.0,256.0,256.0*256.0,1.0));\n"
" texcol = round(EncodedDepth * (16777216.0/16777215.0) * vec4(255.0,255.0,255.0,15.0)) / vec4(255.0,255.0,255.0,15.0);\n"
" ocol0 = texcol * mat4(colmat[0], colmat[1], colmat[2], colmat[3]) + colmat[4];"
"}\n";
const char *VProgram =
"ATTRIN vec2 rawpos;\n"
"ATTRIN vec2 tex0;\n"
"VARYOUT vec2 uv0;\n"
"void main()\n"
"{\n"
" uv0 = tex0;\n"
" gl_Position = vec4(rawpos,0,1);\n"
"}\n";
ProgramShaderCache::CompileShader(s_ColorMatrixProgram, VProgram, pColorMatrixProg);
ProgramShaderCache::CompileShader(s_DepthMatrixProgram, VProgram, pDepthMatrixProg);
s_ColorMatrixUniform = glGetUniformLocation(s_ColorMatrixProgram.glprogid, "colmat");
s_DepthMatrixUniform = glGetUniformLocation(s_DepthMatrixProgram.glprogid, "colmat");
s_ColorCbufid = -1;
s_DepthCbufid = -1;
s_ActiveTexture = -1;
s_NextStage = -1;
for(int i=0; i<8; i++)
s_Textures[i] = -1;
}
TextureCache::~TextureCache()
{
s_ColorMatrixProgram.Destroy();
s_DepthMatrixProgram.Destroy();
for(std::map<u64, VBOCache>::iterator it = s_VBO.begin(); it != s_VBO.end(); it++) {
glDeleteBuffers(1, &it->second.vbo);
glDeleteVertexArrays(1, &it->second.vao);
}
s_VBO.clear();
}
void TextureCache::DisableStage(unsigned int stage)
{
}
void TextureCache::SetStage ()
{
// -1 is the initial value as we don't know which testure should be bound
if(s_ActiveTexture != (u32)-1)
glActiveTexture(GL_TEXTURE0 + s_ActiveTexture);
}
void TextureCache::SetNextStage ( unsigned int stage )
{
s_NextStage = stage;
}
}

View File

@ -0,0 +1,71 @@
// Copyright 2013 Dolphin Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.
#ifndef _TEXTUREMNGR_H_
#define _TEXTUREMNGR_H_
#include <map>
#include "VideoCommon.h"
#include "GLUtil.h"
#include "BPStructs.h"
#include "TextureCacheBase.h"
namespace OGL
{
class TextureCache : public ::TextureCache
{
public:
TextureCache();
static void DisableStage(unsigned int stage);
static void SetStage();
static void SetNextStage(unsigned int stage);
private:
struct TCacheEntry : TCacheEntryBase
{
GLuint texture;
GLuint framebuffer;
PC_TexFormat pcfmt;
int gl_format;
int gl_iformat;
int gl_type;
int m_tex_levels;
//TexMode0 mode; // current filter and clamp modes that texture is set to
//TexMode1 mode1; // current filter and clamp modes that texture is set to
TCacheEntry();
~TCacheEntry();
void Load(unsigned int width, unsigned int height,
unsigned int expanded_width, unsigned int level);
void FromRenderTarget(u32 dstAddr, unsigned int dstFormat,
unsigned int srcFormat, const EFBRectangle& srcRect,
bool isIntensity, bool scaleByHalf, unsigned int cbufid,
const float *colmat);
void Bind(unsigned int stage);
bool Save(const char filename[], unsigned int level);
};
~TextureCache();
TCacheEntryBase* CreateTexture(unsigned int width, unsigned int height,
unsigned int expanded_width, unsigned int tex_levels, PC_TexFormat pcfmt);
TCacheEntryBase* CreateRenderTargetTexture(unsigned int scaled_tex_w, unsigned int scaled_tex_h);
};
bool SaveTexture(const char* filename, u32 textarget, u32 tex, int virtual_width, int virtual_height, unsigned int level);
}
#endif // _TEXTUREMNGR_H_

View File

@ -0,0 +1,440 @@
// Copyright 2013 Dolphin Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.
// Fast image conversion using OpenGL shaders.
// This kind of stuff would be a LOT nicer with OpenCL.
#include <math.h>
#include "TextureConverter.h"
#include "TextureConversionShader.h"
#include "TextureCache.h"
#include "ProgramShaderCache.h"
#include "VertexShaderManager.h"
#include "FramebufferManager.h"
#include "Globals.h"
#include "VideoConfig.h"
#include "ImageWrite.h"
#include "Render.h"
#include "FileUtil.h"
#include "HW/Memmap.h"
namespace OGL
{
namespace TextureConverter
{
using OGL::TextureCache;
static GLuint s_texConvFrameBuffer = 0;
static GLuint s_srcTexture = 0; // for decoding from RAM
static GLuint s_srcTextureWidth = 0;
static GLuint s_srcTextureHeight = 0;
static GLuint s_dstTexture = 0; // for encoding to RAM
const int renderBufferWidth = 1024;
const int renderBufferHeight = 1024;
static SHADER s_rgbToYuyvProgram;
static SHADER s_yuyvToRgbProgram;
// Not all slots are taken - but who cares.
const u32 NUM_ENCODING_PROGRAMS = 64;
static SHADER s_encodingPrograms[NUM_ENCODING_PROGRAMS];
static GLuint s_encode_VBO = 0;
static GLuint s_encode_VAO = 0;
static GLuint s_decode_VBO = 0;
static GLuint s_decode_VAO = 0;
static TargetRectangle s_cached_sourceRc;
static int s_cached_srcWidth = 0;
static int s_cached_srcHeight = 0;
static const char *VProgram =
"ATTRIN vec2 rawpos;\n"
"ATTRIN vec2 tex0;\n"
"VARYOUT vec2 uv0;\n"
"void main()\n"
"{\n"
" uv0 = tex0;\n"
" gl_Position = vec4(rawpos, 0.0, 1.0);\n"
"}\n";
void CreatePrograms()
{
// Output is BGRA because that is slightly faster than RGBA.
const char *FProgramRgbToYuyv =
"uniform sampler2DRect samp9;\n"
"VARYIN vec2 uv0;\n"
"COLOROUT(ocol0)\n"
"void main()\n"
"{\n"
" vec3 c0 = texture2DRect(samp9, uv0).rgb;\n"
" vec3 c1 = texture2DRect(samp9, uv0 + vec2(1.0, 0.0)).rgb;\n"
" vec3 c01 = (c0 + c1) * 0.5;\n"
" vec3 y_const = vec3(0.257,0.504,0.098);\n"
" vec3 u_const = vec3(-0.148,-0.291,0.439);\n"
" vec3 v_const = vec3(0.439,-0.368,-0.071);\n"
" vec4 const3 = vec4(0.0625,0.5,0.0625,0.5);\n"
" ocol0 = vec4(dot(c1,y_const),dot(c01,u_const),dot(c0,y_const),dot(c01, v_const)) + const3;\n"
"}\n";
const char *FProgramYuyvToRgb =
"uniform sampler2DRect samp9;\n"
"VARYIN vec2 uv0;\n"
"COLOROUT(ocol0)\n"
"void main()\n"
"{\n"
" vec4 c0 = texture2DRect(samp9, uv0).rgba;\n"
" float f = step(0.5, fract(uv0.x));\n"
" float y = mix(c0.b, c0.r, f);\n"
" float yComp = 1.164 * (y - 0.0625);\n"
" float uComp = c0.g - 0.5;\n"
" float vComp = c0.a - 0.5;\n"
" ocol0 = vec4(yComp + (1.596 * vComp),\n"
" yComp - (0.813 * vComp) - (0.391 * uComp),\n"
" yComp + (2.018 * uComp),\n"
" 1.0);\n"
"}\n";
ProgramShaderCache::CompileShader(s_rgbToYuyvProgram, VProgram, FProgramRgbToYuyv);
ProgramShaderCache::CompileShader(s_yuyvToRgbProgram, VProgram, FProgramYuyvToRgb);
}
SHADER &GetOrCreateEncodingShader(u32 format)
{
if (format > NUM_ENCODING_PROGRAMS)
{
PanicAlert("Unknown texture copy format: 0x%x\n", format);
return s_encodingPrograms[0];
}
if (s_encodingPrograms[format].glprogid == 0)
{
const char* shader = TextureConversionShader::GenerateEncodingShader(format, API_OPENGL);
#if defined(_DEBUG) || defined(DEBUGFAST)
if (g_ActiveConfig.iLog & CONF_SAVESHADERS && shader)
{
static int counter = 0;
char szTemp[MAX_PATH];
sprintf(szTemp, "%senc_%04i.txt", File::GetUserPath(D_DUMP_IDX).c_str(), counter++);
SaveData(szTemp, shader);
}
#endif
ProgramShaderCache::CompileShader(s_encodingPrograms[format], VProgram, shader);
}
return s_encodingPrograms[format];
}
void Init()
{
glGenFramebuffers(1, &s_texConvFrameBuffer);
glGenBuffers(1, &s_encode_VBO );
glGenVertexArrays(1, &s_encode_VAO );
glBindBuffer(GL_ARRAY_BUFFER, s_encode_VBO );
glBindVertexArray( s_encode_VAO );
glEnableVertexAttribArray(SHADER_POSITION_ATTRIB);
glVertexAttribPointer(SHADER_POSITION_ATTRIB, 2, GL_FLOAT, 0, sizeof(GLfloat)*4, (GLfloat*)NULL);
glEnableVertexAttribArray(SHADER_TEXTURE0_ATTRIB);
glVertexAttribPointer(SHADER_TEXTURE0_ATTRIB, 2, GL_FLOAT, 0, sizeof(GLfloat)*4, (GLfloat*)NULL+2);
s_cached_sourceRc.top = -1;
s_cached_sourceRc.bottom = -1;
s_cached_sourceRc.left = -1;
s_cached_sourceRc.right = -1;
glGenBuffers(1, &s_decode_VBO );
glGenVertexArrays(1, &s_decode_VAO );
glBindBuffer(GL_ARRAY_BUFFER, s_decode_VBO );
glBindVertexArray( s_decode_VAO );
s_cached_srcWidth = -1;
s_cached_srcHeight = -1;
glEnableVertexAttribArray(SHADER_POSITION_ATTRIB);
glVertexAttribPointer(SHADER_POSITION_ATTRIB, 2, GL_FLOAT, 0, sizeof(GLfloat)*4, (GLfloat*)NULL);
glEnableVertexAttribArray(SHADER_TEXTURE0_ATTRIB);
glVertexAttribPointer(SHADER_TEXTURE0_ATTRIB, 2, GL_FLOAT, 0, sizeof(GLfloat)*4, (GLfloat*)NULL+2);
s_srcTextureWidth = 0;
s_srcTextureHeight = 0;
glActiveTexture(GL_TEXTURE0 + 9);
glGenTextures(1, &s_srcTexture);
glBindTexture(getFbType(), s_srcTexture);
glTexParameteri(getFbType(), GL_TEXTURE_MAX_LEVEL, 0);
glGenTextures(1, &s_dstTexture);
glBindTexture(GL_TEXTURE_2D, s_dstTexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, renderBufferWidth, renderBufferHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
CreatePrograms();
}
void Shutdown()
{
glDeleteTextures(1, &s_srcTexture);
glDeleteTextures(1, &s_dstTexture);
glDeleteFramebuffers(1, &s_texConvFrameBuffer);
glDeleteBuffers(1, &s_encode_VBO );
glDeleteVertexArrays(1, &s_encode_VAO );
glDeleteBuffers(1, &s_decode_VBO );
glDeleteVertexArrays(1, &s_decode_VAO );
s_rgbToYuyvProgram.Destroy();
s_yuyvToRgbProgram.Destroy();
for (unsigned int i = 0; i < NUM_ENCODING_PROGRAMS; i++)
s_encodingPrograms[i].Destroy();
s_srcTexture = 0;
s_dstTexture = 0;
s_texConvFrameBuffer = 0;
}
void EncodeToRamUsingShader(GLuint srcTexture, const TargetRectangle& sourceRc,
u8* destAddr, int dstWidth, int dstHeight, int readStride,
bool toTexture, bool linearFilter)
{
// switch to texture converter frame buffer
// attach render buffer as color destination
FramebufferManager::SetFramebuffer(s_texConvFrameBuffer);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, s_dstTexture, 0);
GL_REPORT_ERRORD();
// set source texture
glActiveTexture(GL_TEXTURE0+9);
glBindTexture(getFbType(), srcTexture);
if (linearFilter)
{
glTexParameteri(getFbType(), GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(getFbType(), GL_TEXTURE_MIN_FILTER, GL_LINEAR);
}
else
{
glTexParameteri(getFbType(), GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(getFbType(), GL_TEXTURE_MIN_FILTER, GL_NEAREST);
}
GL_REPORT_ERRORD();
glViewport(0, 0, (GLsizei)dstWidth, (GLsizei)dstHeight);
GL_REPORT_ERRORD();
if(!(s_cached_sourceRc == sourceRc)) {
GLfloat vertices[] = {
-1.f, -1.f,
(float)sourceRc.left, (float)sourceRc.top,
-1.f, 1.f,
(float)sourceRc.left, (float)sourceRc.bottom,
1.f, -1.f,
(float)sourceRc.right, (float)sourceRc.top,
1.f, 1.f,
(float)sourceRc.right, (float)sourceRc.bottom
};
glBindBuffer(GL_ARRAY_BUFFER, s_encode_VBO );
glBufferData(GL_ARRAY_BUFFER, 4*4*sizeof(GLfloat), vertices, GL_STREAM_DRAW);
s_cached_sourceRc = sourceRc;
}
glBindVertexArray( s_encode_VAO );
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
GL_REPORT_ERRORD();
// .. and then read back the results.
// TODO: make this less slow.
int writeStride = bpmem.copyMipMapStrideChannels * 32;
if (writeStride != readStride && toTexture)
{
// writing to a texture of a different size
int readHeight = readStride / dstWidth;
readHeight /= 4; // 4 bytes per pixel
int readStart = 0;
int readLoops = dstHeight / readHeight;
for (int i = 0; i < readLoops; i++)
{
glReadPixels(0, readStart, (GLsizei)dstWidth, (GLsizei)readHeight, GL_BGRA, GL_UNSIGNED_BYTE, destAddr);
readStart += readHeight;
destAddr += writeStride;
}
}
else
glReadPixels(0, 0, (GLsizei)dstWidth, (GLsizei)dstHeight, GL_BGRA, GL_UNSIGNED_BYTE, destAddr);
GL_REPORT_ERRORD();
}
int EncodeToRamFromTexture(u32 address,GLuint source_texture, bool bFromZBuffer, bool bIsIntensityFmt, u32 copyfmt, int bScaleByHalf, const EFBRectangle& source)
{
u32 format = copyfmt;
if (bFromZBuffer)
{
format |= _GX_TF_ZTF;
if (copyfmt == 11)
format = GX_TF_Z16;
else if (format < GX_TF_Z8 || format > GX_TF_Z24X8)
format |= _GX_TF_CTF;
}
else
if (copyfmt > GX_TF_RGBA8 || (copyfmt < GX_TF_RGB565 && !bIsIntensityFmt))
format |= _GX_TF_CTF;
SHADER& texconv_shader = GetOrCreateEncodingShader(format);
u8 *dest_ptr = Memory::GetPointer(address);
int width = (source.right - source.left) >> bScaleByHalf;
int height = (source.bottom - source.top) >> bScaleByHalf;
int size_in_bytes = TexDecoder_GetTextureSizeInBytes(width, height, format);
u16 blkW = TexDecoder_GetBlockWidthInTexels(format) - 1;
u16 blkH = TexDecoder_GetBlockHeightInTexels(format) - 1;
u16 samples = TextureConversionShader::GetEncodedSampleCount(format);
// only copy on cache line boundaries
// extra pixels are copied but not displayed in the resulting texture
s32 expandedWidth = (width + blkW) & (~blkW);
s32 expandedHeight = (height + blkH) & (~blkH);
float sampleStride = bScaleByHalf ? 2.f : 1.f;
float params[] = {
Renderer::EFBToScaledXf(sampleStride), Renderer::EFBToScaledYf(sampleStride),
0.0f, 0.0f,
(float)expandedWidth, (float)Renderer::EFBToScaledY(expandedHeight)-1,
(float)Renderer::EFBToScaledX(source.left), (float)Renderer::EFBToScaledY(EFB_HEIGHT - source.top - expandedHeight)
};
texconv_shader.Bind();
glUniform4fv(texconv_shader.UniformLocations[C_COLORS], 2, params);
TargetRectangle scaledSource;
scaledSource.top = 0;
scaledSource.bottom = expandedHeight;
scaledSource.left = 0;
scaledSource.right = expandedWidth / samples;
int cacheBytes = 32;
if ((format & 0x0f) == 6)
cacheBytes = 64;
int readStride = (expandedWidth * cacheBytes) /
TexDecoder_GetBlockWidthInTexels(format);
EncodeToRamUsingShader(source_texture, scaledSource,
dest_ptr, expandedWidth / samples, expandedHeight, readStride,
true, bScaleByHalf > 0 && !bFromZBuffer);
return size_in_bytes; // TODO: D3D11 is calculating this value differently!
}
void EncodeToRamYUYV(GLuint srcTexture, const TargetRectangle& sourceRc, u8* destAddr, int dstWidth, int dstHeight)
{
g_renderer->ResetAPIState();
s_rgbToYuyvProgram.Bind();
EncodeToRamUsingShader(srcTexture, sourceRc, destAddr, dstWidth / 2, dstHeight, 0, false, false);
FramebufferManager::SetFramebuffer(0);
VertexShaderManager::SetViewportChanged();
TextureCache::DisableStage(0);
g_renderer->RestoreAPIState();
GL_REPORT_ERRORD();
}
// Should be scale free.
void DecodeToTexture(u32 xfbAddr, int srcWidth, int srcHeight, GLuint destTexture)
{
u8* srcAddr = Memory::GetPointer(xfbAddr);
if (!srcAddr)
{
WARN_LOG(VIDEO, "Tried to decode from invalid memory address");
return;
}
int srcFmtWidth = srcWidth / 2;
g_renderer->ResetAPIState(); // reset any game specific settings
// switch to texture converter frame buffer
// attach destTexture as color destination
FramebufferManager::SetFramebuffer(s_texConvFrameBuffer);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, destTexture, 0);
GL_REPORT_FBO_ERROR();
// activate source texture
// set srcAddr as data for source texture
glActiveTexture(GL_TEXTURE0+9);
glBindTexture(getFbType(), s_srcTexture);
// TODO: make this less slow. (How?)
if ((GLsizei)s_srcTextureWidth == (GLsizei)srcFmtWidth && (GLsizei)s_srcTextureHeight == (GLsizei)srcHeight)
{
glTexSubImage2D(getFbType(), 0,0,0,s_srcTextureWidth, s_srcTextureHeight,
GL_BGRA, GL_UNSIGNED_BYTE, srcAddr);
}
else
{
glTexImage2D(getFbType(), 0, GL_RGBA8, (GLsizei)srcFmtWidth, (GLsizei)srcHeight,
0, GL_BGRA, GL_UNSIGNED_BYTE, srcAddr);
s_srcTextureWidth = (GLsizei)srcFmtWidth;
s_srcTextureHeight = (GLsizei)srcHeight;
}
glViewport(0, 0, srcWidth, srcHeight);
s_yuyvToRgbProgram.Bind();
GL_REPORT_ERRORD();
if(s_cached_srcHeight != srcHeight || s_cached_srcWidth != srcWidth) {
GLfloat vertices[] = {
1.f, -1.f,
(float)srcFmtWidth, (float)srcHeight,
1.f, 1.f,
(float)srcFmtWidth, 0.f,
-1.f, -1.f,
0.f, (float)srcHeight,
-1.f, 1.f,
0.f, 0.f
};
glBindBuffer(GL_ARRAY_BUFFER, s_decode_VBO );
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat)*4*4, vertices, GL_STREAM_DRAW);
s_cached_srcHeight = srcHeight;
s_cached_srcWidth = srcWidth;
}
glBindVertexArray( s_decode_VAO );
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
GL_REPORT_ERRORD();
VertexShaderManager::SetViewportChanged();
FramebufferManager::SetFramebuffer(0);
g_renderer->RestoreAPIState();
GL_REPORT_ERRORD();
}
} // namespace
} // namespace OGL

View File

@ -0,0 +1,34 @@
// Copyright 2013 Dolphin Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.
#ifndef _TEXTURECONVERTER_H_
#define _TEXTURECONVERTER_H_
#include "VideoCommon.h"
#include "GLUtil.h"
namespace OGL
{
// Converts textures between formats using shaders
// TODO: support multiple texture formats
namespace TextureConverter
{
void Init();
void Shutdown();
void EncodeToRamYUYV(GLuint srcTexture, const TargetRectangle& sourceRc,
u8* destAddr, int dstWidth, int dstHeight);
void DecodeToTexture(u32 xfbAddr, int srcWidth, int srcHeight, GLuint destTexture);
// returns size of the encoded data (in bytes)
int EncodeToRamFromTexture(u32 address, GLuint source_texture, bool bFromZBuffer, bool bIsIntensityFmt, u32 copyfmt, int bScaleByHalf, const EFBRectangle& source);
}
} // namespace OGL
#endif // _TEXTURECONVERTER_H_

View File

@ -0,0 +1,355 @@
// Copyright 2013 Dolphin Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.
#include "Globals.h"
#include <fstream>
#include <vector>
#include "Fifo.h"
#include "DriverDetails.h"
#include "VideoConfig.h"
#include "Statistics.h"
#include "MemoryUtil.h"
#include "Render.h"
#include "ImageWrite.h"
#include "BPMemory.h"
#include "TextureCache.h"
#include "PixelShaderManager.h"
#include "VertexShaderManager.h"
#include "ProgramShaderCache.h"
#include "VertexShaderGen.h"
#include "VertexLoader.h"
#include "VertexManager.h"
#include "IndexGenerator.h"
#include "OpcodeDecoding.h"
#include "FileUtil.h"
#include "Debugger.h"
#include "StreamBuffer.h"
#include "PerfQueryBase.h"
#include "Render.h"
#include "main.h"
// internal state for loading vertices
extern NativeVertexFormat *g_nativeVertexFmt;
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 = 16*1024*1024;
static StreamBuffer *s_vertexBuffer;
static StreamBuffer *s_indexBuffer;
static u32 s_baseVertex;
static u32 s_offset[3];
VertexManager::VertexManager()
{
CreateDeviceObjects();
}
VertexManager::~VertexManager()
{
DestroyDeviceObjects();
}
void VertexManager::CreateDeviceObjects()
{
s_vertexBuffer = new StreamBuffer(GL_ARRAY_BUFFER, MAX_VBUFFER_SIZE);
m_vertex_buffers = s_vertexBuffer->getBuffer();
s_indexBuffer = new StreamBuffer(GL_ELEMENT_ARRAY_BUFFER, MAX_IBUFFER_SIZE);
m_index_buffers = s_indexBuffer->getBuffer();
m_CurrentVertexFmt = NULL;
m_last_vao = 0;
}
void VertexManager::DestroyDeviceObjects()
{
GL_REPORT_ERRORD();
glBindBuffer(GL_ARRAY_BUFFER, 0 );
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0 );
GL_REPORT_ERROR();
delete s_vertexBuffer;
delete s_indexBuffer;
GL_REPORT_ERROR();
}
void VertexManager::PrepareDrawBuffers(u32 stride)
{
u32 vertex_data_size = IndexGenerator::GetNumVerts() * stride;
u32 triangle_index_size = IndexGenerator::GetTriangleindexLen();
u32 line_index_size = IndexGenerator::GetLineindexLen();
u32 point_index_size = IndexGenerator::GetPointindexLen();
u32 index_size = (triangle_index_size+line_index_size+point_index_size) * sizeof(u16);
s_vertexBuffer->Alloc(vertex_data_size, stride);
u32 offset = s_vertexBuffer->Upload(GetVertexBuffer(), vertex_data_size);
s_baseVertex = offset / stride;
s_indexBuffer->Alloc(index_size);
if(triangle_index_size)
{
s_offset[0] = s_indexBuffer->Upload((u8*)GetTriangleIndexBuffer(), triangle_index_size * sizeof(u16));
}
if(line_index_size)
{
s_offset[1] = s_indexBuffer->Upload((u8*)GetLineIndexBuffer(), line_index_size * sizeof(u16));
}
if(point_index_size)
{
s_offset[2] = s_indexBuffer->Upload((u8*)GetPointIndexBuffer(), point_index_size * sizeof(u16));
}
ADDSTAT(stats.thisFrame.bytesVertexStreamed, vertex_data_size);
ADDSTAT(stats.thisFrame.bytesIndexStreamed, index_size);
}
void VertexManager::Draw(u32 stride)
{
u32 triangle_index_size = IndexGenerator::GetTriangleindexLen();
u32 line_index_size = IndexGenerator::GetLineindexLen();
u32 point_index_size = IndexGenerator::GetPointindexLen();
u32 max_index = IndexGenerator::GetNumVerts();
GLenum triangle_mode = g_ActiveConfig.backend_info.bSupportsPrimitiveRestart?GL_TRIANGLE_STRIP:GL_TRIANGLES;
if(g_ogl_config.bSupportsGLBaseVertex) {
if (triangle_index_size > 0)
{
glDrawRangeElementsBaseVertex(triangle_mode, 0, max_index, triangle_index_size, GL_UNSIGNED_SHORT, (u8*)NULL+s_offset[0], s_baseVertex);
INCSTAT(stats.thisFrame.numIndexedDrawCalls);
}
if (line_index_size > 0)
{
glDrawRangeElementsBaseVertex(GL_LINES, 0, max_index, line_index_size, GL_UNSIGNED_SHORT, (u8*)NULL+s_offset[1], s_baseVertex);
INCSTAT(stats.thisFrame.numIndexedDrawCalls);
}
if (point_index_size > 0)
{
glDrawRangeElementsBaseVertex(GL_POINTS, 0, max_index, point_index_size, GL_UNSIGNED_SHORT, (u8*)NULL+s_offset[2], s_baseVertex);
INCSTAT(stats.thisFrame.numIndexedDrawCalls);
}
}
else if (DriverDetails::HasBug(DriverDetails::BUG_ISTEGRA))
{
if (triangle_index_size > 0)
{
glDrawElements(triangle_mode, triangle_index_size, GL_UNSIGNED_SHORT, (u8*)NULL+s_offset[0]);
INCSTAT(stats.thisFrame.numIndexedDrawCalls);
}
if (line_index_size > 0)
{
glDrawElements(GL_LINES, line_index_size, GL_UNSIGNED_SHORT, (u8*)NULL+s_offset[1]);
INCSTAT(stats.thisFrame.numIndexedDrawCalls);
}
if (point_index_size > 0)
{
glDrawElements(GL_POINTS, point_index_size, GL_UNSIGNED_SHORT, (u8*)NULL+s_offset[2]);
INCSTAT(stats.thisFrame.numIndexedDrawCalls);
}
} else {
if (triangle_index_size > 0)
{
glDrawRangeElements(triangle_mode, 0, max_index, triangle_index_size, GL_UNSIGNED_SHORT, (u8*)NULL+s_offset[0]);
INCSTAT(stats.thisFrame.numIndexedDrawCalls);
}
if (line_index_size > 0)
{
glDrawRangeElements(GL_LINES, 0, max_index, line_index_size, GL_UNSIGNED_SHORT, (u8*)NULL+s_offset[1]);
INCSTAT(stats.thisFrame.numIndexedDrawCalls);
}
if (point_index_size > 0)
{
glDrawRangeElements(GL_POINTS, 0, max_index, point_index_size, GL_UNSIGNED_SHORT, (u8*)NULL+s_offset[2]);
INCSTAT(stats.thisFrame.numIndexedDrawCalls);
}
}
}
void VertexManager::vFlush()
{
#if defined(_DEBUG) || defined(DEBUGFAST)
PRIM_LOG("frame%d:\n texgen=%d, numchan=%d, dualtex=%d, ztex=%d, cole=%d, alpe=%d, ze=%d", g_ActiveConfig.iSaveTargetId, xfregs.numTexGen.numTexGens,
xfregs.numChan.numColorChans, xfregs.dualTexTrans.enabled, bpmem.ztex2.op,
bpmem.blendmode.colorupdate, bpmem.blendmode.alphaupdate, bpmem.zmode.updateenable);
for (unsigned int i = 0; i < xfregs.numChan.numColorChans; ++i)
{
LitChannel* ch = &xfregs.color[i];
PRIM_LOG("colchan%d: matsrc=%d, light=0x%x, ambsrc=%d, diffunc=%d, attfunc=%d", i, ch->matsource, ch->GetFullLightMask(), ch->ambsource, ch->diffusefunc, ch->attnfunc);
ch = &xfregs.alpha[i];
PRIM_LOG("alpchan%d: matsrc=%d, light=0x%x, ambsrc=%d, diffunc=%d, attfunc=%d", i, ch->matsource, ch->GetFullLightMask(), ch->ambsource, ch->diffusefunc, ch->attnfunc);
}
for (unsigned int i = 0; i < xfregs.numTexGen.numTexGens; ++i)
{
TexMtxInfo tinfo = xfregs.texMtxInfo[i];
if (tinfo.texgentype != XF_TEXGEN_EMBOSS_MAP) tinfo.hex &= 0x7ff;
if (tinfo.texgentype != XF_TEXGEN_REGULAR) tinfo.projection = 0;
PRIM_LOG("txgen%d: proj=%d, input=%d, gentype=%d, srcrow=%d, embsrc=%d, emblght=%d, postmtx=%d, postnorm=%d",
i, tinfo.projection, tinfo.inputform, tinfo.texgentype, tinfo.sourcerow, tinfo.embosssourceshift, tinfo.embosslightshift,
xfregs.postMtxInfo[i].index, xfregs.postMtxInfo[i].normalize);
}
PRIM_LOG("pixel: tev=%d, ind=%d, texgen=%d, dstalpha=%d, alphatest=0x%x", bpmem.genMode.numtevstages+1, bpmem.genMode.numindstages,
bpmem.genMode.numtexgens, (u32)bpmem.dstalpha.enable, (bpmem.alpha_test.hex>>16)&0xff);
#endif
(void)GL_REPORT_ERROR();
GLVertexFormat *nativeVertexFmt = (GLVertexFormat*)g_nativeVertexFmt;
u32 stride = nativeVertexFmt->GetVertexStride();
if(m_last_vao != nativeVertexFmt->VAO) {
glBindVertexArray(nativeVertexFmt->VAO);
m_last_vao = nativeVertexFmt->VAO;
}
PrepareDrawBuffers(stride);
GL_REPORT_ERRORD();
u32 usedtextures = 0;
for (u32 i = 0; i < (u32)bpmem.genMode.numtevstages + 1; ++i)
if (bpmem.tevorders[i / 2].getEnable(i & 1))
usedtextures |= 1 << bpmem.tevorders[i/2].getTexMap(i & 1);
if (bpmem.genMode.numindstages > 0)
for (u32 i = 0; i < (u32)bpmem.genMode.numtevstages + 1; ++i)
if (bpmem.tevind[i].IsActive() && bpmem.tevind[i].bt < bpmem.genMode.numindstages)
usedtextures |= 1 << bpmem.tevindref.getTexMap(bpmem.tevind[i].bt);
for (u32 i = 0; i < 8; i++)
{
if (usedtextures & (1 << i))
{
TextureCache::SetNextStage(i);
g_renderer->SetSamplerState(i % 4, i / 4);
FourTexUnits &tex = bpmem.tex[i >> 2];
TextureCache::TCacheEntryBase* tentry = TextureCache::Load(i,
(tex.texImage3[i&3].image_base/* & 0x1FFFFF*/) << 5,
tex.texImage0[i&3].width + 1, tex.texImage0[i&3].height + 1,
tex.texImage0[i&3].format, tex.texTlut[i&3].tmem_offset<<9,
tex.texTlut[i&3].tlut_format,
(tex.texMode0[i&3].min_filter & 3),
(tex.texMode1[i&3].max_lod + 0xf) / 0x10,
tex.texImage1[i&3].image_type);
if (tentry)
{
// 0s are probably for no manual wrapping needed.
PixelShaderManager::SetTexDims(i, tentry->native_width, tentry->native_height, 0, 0);
}
else
ERROR_LOG(VIDEO, "Error loading texture");
}
}
bool useDstAlpha = !g_ActiveConfig.bDstAlphaPass && bpmem.dstalpha.enable && bpmem.blendmode.alphaupdate
&& bpmem.zcontrol.pixel_format == PIXELFMT_RGBA6_Z24;
// Makes sure we can actually do Dual source blending
bool dualSourcePossible = g_ActiveConfig.backend_info.bSupportsDualSourceBlend;
// finally bind
if (dualSourcePossible)
{
if (useDstAlpha)
{
// If host supports GL_ARB_blend_func_extended, we can do dst alpha in
// the same pass as regular rendering.
ProgramShaderCache::SetShader(DSTALPHA_DUAL_SOURCE_BLEND, g_nativeVertexFmt->m_components);
}
else
{
ProgramShaderCache::SetShader(DSTALPHA_NONE,g_nativeVertexFmt->m_components);
}
}
else
{
ProgramShaderCache::SetShader(DSTALPHA_NONE,g_nativeVertexFmt->m_components);
}
// set global constants
VertexShaderManager::SetConstants();
PixelShaderManager::SetConstants(g_nativeVertexFmt->m_components);
ProgramShaderCache::UploadConstants();
// setup the pointers
if (g_nativeVertexFmt)
g_nativeVertexFmt->SetupVertexPointers();
GL_REPORT_ERRORD();
g_perf_query->EnableQuery(bpmem.zcontrol.early_ztest ? PQG_ZCOMP_ZCOMPLOC : PQG_ZCOMP);
Draw(stride);
g_perf_query->DisableQuery(bpmem.zcontrol.early_ztest ? PQG_ZCOMP_ZCOMPLOC : PQG_ZCOMP);
//ERROR_LOG(VIDEO, "PerfQuery result: %d", g_perf_query->GetQueryResult(bpmem.zcontrol.early_ztest ? PQ_ZCOMP_OUTPUT_ZCOMPLOC : PQ_ZCOMP_OUTPUT));
// run through vertex groups again to set alpha
if (useDstAlpha && !dualSourcePossible)
{
ProgramShaderCache::SetShader(DSTALPHA_ALPHA_PASS,g_nativeVertexFmt->m_components);
if (!g_ActiveConfig.backend_info.bSupportsGLSLUBO)
{
// Need to set these again, if we don't support UBO
VertexShaderManager::SetConstants();
PixelShaderManager::SetConstants(g_nativeVertexFmt->m_components);
}
// only update alpha
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE);
glDisable(GL_BLEND);
Draw(stride);
// restore color mask
g_renderer->SetColorMask();
if (bpmem.blendmode.blendenable || bpmem.blendmode.subtract)
glEnable(GL_BLEND);
}
GFX_DEBUGGER_PAUSE_AT(NEXT_FLUSH, true);
#if defined(_DEBUG) || defined(DEBUGFAST)
if (g_ActiveConfig.iLog & CONF_SAVESHADERS)
{
// save the shaders
ProgramShaderCache::PCacheEntry prog = ProgramShaderCache::GetShaderProgram();
char strfile[255];
sprintf(strfile, "%sps%.3d.txt", File::GetUserPath(D_DUMPFRAMES_IDX).c_str(), g_ActiveConfig.iSaveTargetId);
std::ofstream fps;
OpenFStream(fps, strfile, std::ios_base::out);
fps << prog.shader.strpprog.c_str();
sprintf(strfile, "%svs%.3d.txt", File::GetUserPath(D_DUMPFRAMES_IDX).c_str(), g_ActiveConfig.iSaveTargetId);
std::ofstream fvs;
OpenFStream(fvs, strfile, std::ios_base::out);
fvs << prog.shader.strvprog.c_str();
}
if (g_ActiveConfig.iLog & CONF_SAVETARGETS)
{
char str[128];
sprintf(str, "%starg%.3d.tga", File::GetUserPath(D_DUMPFRAMES_IDX).c_str(), g_ActiveConfig.iSaveTargetId);
TargetRectangle tr;
tr.left = 0;
tr.right = Renderer::GetTargetWidth();
tr.top = 0;
tr.bottom = Renderer::GetTargetHeight();
g_renderer->SaveScreenshot(str, tr);
}
#endif
g_Config.iSaveTargetId++;
ClearEFBCache();
GL_REPORT_ERRORD();
}
} // namespace

View File

@ -0,0 +1,52 @@
// Copyright 2013 Dolphin Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.
#ifndef _VERTEXMANAGER_H_
#define _VERTEXMANAGER_H_
#include "CPMemory.h"
#include "VertexManagerBase.h"
namespace OGL
{
class GLVertexFormat : public NativeVertexFormat
{
PortableVertexDeclaration vtx_decl;
public:
GLVertexFormat();
~GLVertexFormat();
virtual void Initialize(const PortableVertexDeclaration &_vtx_decl);
virtual void SetupVertexPointers();
GLuint VAO;
};
// Handles the OpenGL details of drawing lots of vertices quickly.
// Other functionality is moving out.
class VertexManager : public ::VertexManager
{
public:
VertexManager();
~VertexManager();
NativeVertexFormat* CreateNativeVertexFormat();
void CreateDeviceObjects();
void DestroyDeviceObjects();
// NativeVertexFormat use this
GLuint m_vertex_buffers;
GLuint m_index_buffers;
GLuint m_last_vao;
private:
void Draw(u32 stride);
void vFlush();
void PrepareDrawBuffers(u32 stride);
NativeVertexFormat *m_CurrentVertexFmt;
};
}
#endif // _VERTEXMANAGER_H_

View File

@ -0,0 +1,130 @@
// Copyright 2013 Dolphin Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.
#include <math.h>
#include "Globals.h"
#include "VideoConfig.h"
#include "Statistics.h"
#include "GLUtil.h"
#include "Render.h"
#include "VertexShaderGen.h"
#include "VertexShaderManager.h"
#include "ProgramShaderCache.h"
#include "VertexManager.h"
#include "VertexLoader.h"
#include "XFMemory.h"
#include "ImageWrite.h"
#include "FileUtil.h"
#include "Debugger.h"
namespace OGL
{
void SetVSConstant4fvByName(const char * name, unsigned int offset, const float *f, const unsigned int count = 1)
{
ProgramShaderCache::PCacheEntry tmp = ProgramShaderCache::GetShaderProgram();
for (int a = 0; a < NUM_UNIFORMS; ++a)
{
if (!strcmp(name, UniformNames[a]))
{
if (tmp.shader.UniformLocations[a] == -1)
return;
else if (tmp.shader.UniformSize[a] <= offset)
return;
else
{
unsigned int maxcount= tmp.shader.UniformSize[a]-offset;
glUniform4fv(tmp.shader.UniformLocations[a] + offset, std::min(count, maxcount), f);
return;
}
}
}
}
void Renderer::SetVSConstant4f(unsigned int const_number, float f1, float f2, float f3, float f4)
{
float const buf[4] = {f1, f2, f3, f4};
if (g_ActiveConfig.backend_info.bSupportsGLSLUBO)
{
ProgramShaderCache::SetMultiVSConstant4fv(const_number, buf, 1);
return;
}
for (unsigned int a = 0; a < 9; ++a)
{
if (const_number >= VSVar_Loc[a].reg && const_number < ( VSVar_Loc[a].reg + VSVar_Loc[a].size))
{
unsigned int offset = const_number - VSVar_Loc[a].reg;
SetVSConstant4fvByName(VSVar_Loc[a].name, offset, buf);
return;
}
}
}
void Renderer::SetVSConstant4fv(unsigned int const_number, const float *f)
{
if (g_ActiveConfig.backend_info.bSupportsGLSLUBO)
{
ProgramShaderCache::SetMultiVSConstant4fv(const_number, f, 1);
return;
}
for (unsigned int a = 0; a < 9; ++a)
{
if (const_number >= VSVar_Loc[a].reg && const_number < ( VSVar_Loc[a].reg + VSVar_Loc[a].size))
{
unsigned int offset = const_number - VSVar_Loc[a].reg;
SetVSConstant4fvByName(VSVar_Loc[a].name, offset, f);
return;
}
}
}
void Renderer::SetMultiVSConstant4fv(unsigned int const_number, unsigned int count, const float *f)
{
if (g_ActiveConfig.backend_info.bSupportsGLSLUBO)
{
ProgramShaderCache::SetMultiVSConstant4fv(const_number, f, count);
return;
}
for (unsigned int a = 0; a < 9; ++a)
{
if (const_number >= VSVar_Loc[a].reg && const_number < ( VSVar_Loc[a].reg + VSVar_Loc[a].size))
{
unsigned int offset = const_number - VSVar_Loc[a].reg;
SetVSConstant4fvByName(VSVar_Loc[a].name, offset, f, count);
return;
}
}
}
void Renderer::SetMultiVSConstant3fv(unsigned int const_number, unsigned int count, const float *f)
{
float buf[4 * C_VENVCONST_END];
for (unsigned int i = 0; i < count; i++)
{
buf[4*i ] = *f++;
buf[4*i+1] = *f++;
buf[4*i+2] = *f++;
buf[4*i+3] = 0.f;
}
if (g_ActiveConfig.backend_info.bSupportsGLSLUBO)
{
ProgramShaderCache::SetMultiVSConstant4fv(const_number, buf, count);
return;
}
for (unsigned int a = 0; a < 9; ++a)
{
if (const_number >= VSVar_Loc[a].reg && const_number < ( VSVar_Loc[a].reg + VSVar_Loc[a].size))
{
unsigned int offset = const_number - VSVar_Loc[a].reg;
SetVSConstant4fvByName(VSVar_Loc[a].name, offset, buf, count);
return;
}
}
}
} // namespace OGL

View File

@ -0,0 +1,29 @@
#ifndef OGL_VIDEO_BACKEND_H_
#define OGL_VIDEO_BACKEND_H_
#include "VideoBackendBase.h"
namespace OGL
{
class VideoBackend : public VideoBackendHardware
{
bool Initialize(void *&);
void Shutdown();
std::string GetName();
std::string GetDisplayName();
void Video_Prepare();
void Video_Cleanup();
void ShowConfig(void* parent);
void UpdateFPSDisplay(const char*);
unsigned int PeekMessages();
};
}
#endif

View File

@ -0,0 +1,277 @@
// Copyright 2013 Dolphin Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.
// OpenGL Backend Documentation
/*
1.1 Display settings
Internal and fullscreen resolution: Since the only internal resolutions allowed
are also fullscreen resolution allowed by the system there is only need for one
resolution setting that applies to both the internal resolution and the
fullscreen resolution. - Apparently no, someone else doesn't agree
Todo: Make the internal resolution option apply instantly, currently only the
native and 2x option applies instantly. To do this we need to be able to change
the reinitialize FramebufferManager:Init() while a game is running.
1.2 Screenshots
The screenshots should be taken from the internal representation of the picture
regardless of what the current window size is. Since AA and wireframe is
applied together with the picture resizing this rule is not currently applied
to AA or wireframe pictures, they are instead taken from whatever the window
size is.
Todo: Render AA and wireframe to a separate picture used for the screenshot in
addition to the one for display.
1.3 AA
Make AA apply instantly during gameplay if possible
*/
#include "Globals.h"
#include "Atomic.h"
#include "CommonPaths.h"
#include "Thread.h"
#include "LogManager.h"
#include <cstdarg>
#include <algorithm>
#ifdef _WIN32
#include "EmuWindow.h"
#include "IniFile.h"
#endif
#if defined(HAVE_WX) && HAVE_WX
#include "VideoConfigDiag.h"
#include "Debugger/DebuggerPanel.h"
#endif // HAVE_WX
#include "MainBase.h"
#include "VideoConfig.h"
#include "LookUpTables.h"
#include "ImageWrite.h"
#include "Render.h"
#include "GLUtil.h"
#include "Fifo.h"
#include "OpcodeDecoding.h"
#include "TextureCache.h"
#include "BPStructs.h"
#include "VertexLoader.h"
#include "VertexLoaderManager.h"
#include "VertexManager.h"
#include "PixelShaderManager.h"
#include "VertexShaderManager.h"
#include "ProgramShaderCache.h"
#include "CommandProcessor.h"
#include "PixelEngine.h"
#include "TextureConverter.h"
#include "PostProcessing.h"
#include "OnScreenDisplay.h"
#include "DLCache.h"
#include "FramebufferManager.h"
#include "Core.h"
#include "Host.h"
#include "SamplerCache.h"
#include "PerfQuery.h"
#include "VideoState.h"
#include "IndexGenerator.h"
#include "VideoBackend.h"
#include "ConfigManager.h"
namespace OGL
{
std::string VideoBackend::GetName()
{
return "OGL";
}
std::string VideoBackend::GetDisplayName()
{
return "OpenGL";
}
void GetShaders(std::vector<std::string> &shaders)
{
std::set<std::string> already_found;
shaders.clear();
static const std::string directories[] = {
File::GetUserPath(D_SHADERS_IDX),
File::GetSysDirectory() + SHADERS_DIR DIR_SEP,
};
for (size_t i = 0; i < ArraySize(directories); ++i)
{
if (!File::IsDirectory(directories[i]))
continue;
File::FSTEntry entry;
File::ScanDirectoryTree(directories[i], entry);
for (u32 j = 0; j < entry.children.size(); j++)
{
std::string name = entry.children[j].virtualName.c_str();
if (name.size() < 5)
continue;
if (strcasecmp(name.substr(name.size() - 5).c_str(), ".glsl"))
continue;
name = name.substr(0, name.size() - 5);
if (already_found.find(name) != already_found.end())
continue;
already_found.insert(name);
shaders.push_back(name);
}
}
std::sort(shaders.begin(), shaders.end());
}
void InitBackendInfo()
{
g_Config.backend_info.APIType = API_OPENGL;
g_Config.backend_info.bUseRGBATextures = true;
g_Config.backend_info.bUseMinimalMipCount = false;
g_Config.backend_info.bSupports3DVision = false;
//g_Config.backend_info.bSupportsDualSourceBlend = true; // is gpu dependent and must be set in renderer
g_Config.backend_info.bSupportsFormatReinterpretation = true;
g_Config.backend_info.bSupportsPixelLighting = true;
//g_Config.backend_info.bSupportsEarlyZ = true; // is gpu dependent and must be set in renderer
// aamodes
const char* caamodes[] = {_trans("None"), "2x", "4x", "8x", "8x CSAA", "8xQ CSAA", "16x CSAA", "16xQ CSAA", "4x SSAA"};
g_Config.backend_info.AAModes.assign(caamodes, caamodes + sizeof(caamodes)/sizeof(*caamodes));
// pp shaders
GetShaders(g_Config.backend_info.PPShaders);
}
void VideoBackend::ShowConfig(void *_hParent)
{
#if defined(HAVE_WX) && HAVE_WX
InitBackendInfo();
VideoConfigDiag diag((wxWindow*)_hParent, "OpenGL", "gfx_opengl");
diag.ShowModal();
#endif
}
bool VideoBackend::Initialize(void *&window_handle)
{
InitializeShared();
InitBackendInfo();
frameCount = 0;
g_Config.Load((File::GetUserPath(D_CONFIG_IDX) + "gfx_opengl.ini").c_str());
g_Config.GameIniLoad();
g_Config.UpdateProjectionHack();
g_Config.VerifyValidity();
UpdateActiveConfig();
InitInterface();
if (!GLInterface->Create(window_handle))
return false;
// Do our OSD callbacks
OSD::DoCallbacks(OSD::OSD_INIT);
s_BackendInitialized = true;
return true;
}
// This is called after Initialize() from the Core
// Run from the graphics thread
void VideoBackend::Video_Prepare()
{
GLInterface->MakeCurrent();
g_renderer = new Renderer;
s_efbAccessRequested = false;
s_FifoShuttingDown = false;
s_swapRequested = false;
CommandProcessor::Init();
PixelEngine::Init();
BPInit();
g_vertex_manager = new VertexManager;
g_perf_query = new PerfQuery;
Fifo_Init(); // must be done before OpcodeDecoder_Init()
OpcodeDecoder_Init();
IndexGenerator::Init();
VertexShaderManager::Init();
PixelShaderManager::Init();
ProgramShaderCache::Init();
PostProcessing::Init();
g_texture_cache = new TextureCache();
g_sampler_cache = new SamplerCache();
Renderer::Init();
GL_REPORT_ERRORD();
VertexLoaderManager::Init();
TextureConverter::Init();
#ifndef _M_GENERIC
DLCache::Init();
#endif
// Notify the core that the video backend is ready
Host_Message(WM_USER_CREATE);
}
void VideoBackend::Shutdown()
{
s_BackendInitialized = false;
// Do our OSD callbacks
OSD::DoCallbacks(OSD::OSD_SHUTDOWN);
GLInterface->Shutdown();
}
void VideoBackend::Video_Cleanup() {
if (g_renderer)
{
s_efbAccessRequested = false;
s_FifoShuttingDown = false;
s_swapRequested = false;
#ifndef _M_GENERIC
DLCache::Shutdown();
#endif
Fifo_Shutdown();
// The following calls are NOT Thread Safe
// And need to be called from the video thread
Renderer::Shutdown();
TextureConverter::Shutdown();
VertexLoaderManager::Shutdown();
delete g_sampler_cache;
g_sampler_cache = NULL;
delete g_texture_cache;
g_texture_cache = NULL;
PostProcessing::Shutdown();
ProgramShaderCache::Shutdown();
VertexShaderManager::Shutdown();
PixelShaderManager::Shutdown();
delete g_perf_query;
g_perf_query = NULL;
delete g_vertex_manager;
g_vertex_manager = NULL;
OpcodeDecoder_Shutdown();
delete g_renderer;
g_renderer = NULL;
GLInterface->ClearCurrent();
}
}
}

View File

@ -0,0 +1,10 @@
// Copyright 2013 Dolphin Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.
#ifndef _MAIN_H_
#define _MAIN_H_
#include "MainBase.h"
#endif // _MAIN_H_

View File

@ -0,0 +1,5 @@
// Copyright 2013 Dolphin Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.
#include "stdafx.h"

View File

@ -0,0 +1,12 @@
// Copyright 2013 Dolphin Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.
#pragma once
#define _WIN32_WINNT 0x501
#ifndef _WIN32_IE
#define _WIN32_IE 0x0500 // Default value is 0x0400
#endif
#include <tchar.h>
#include <windows.h>