snes9x/win32/COpenGL.cpp

854 lines
22 KiB
C++
Raw Permalink Normal View History

/*****************************************************************************\
Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
This file is licensed under the Snes9x License.
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#include "COpenGL.h"
#include "win32_display.h"
#include "../snes9x.h"
#include "../gfx.h"
#include "../display.h"
#include "wsnes9x.h"
#include <msxml2.h>
#include "../filter/hq2x.h"
#include "../filter/2xsai.h"
#include "snes9x_imgui.h"
#include "imgui_impl_opengl3.h"
COpenGL::COpenGL(void)
{
hDC = NULL;
hRC = NULL;
hWnd = NULL;
drawTexture = 0;
initDone = false;
afterRenderWidth = 0;
afterRenderHeight = 0;
outTextureWidth = 0;
outTextureHeight = 0;
fullscreen = false;
shaderFunctionsLoaded = false;
shader_type = OGL_SHADER_NONE;
pboFunctionsLoaded = false;
shaderProgram = 0;
vertexShader = 0;
fragmentShader = 0;
cgContext = NULL;
cgVertexProgram = cgFragmentProgram = NULL;
cgAvailable = false;
frameCount = 0;
cgShader = NULL;
2018-05-20 17:48:38 +00:00
glslShader = NULL;
*currentShaderFile = _T('\0');
}
COpenGL::~COpenGL(void)
{
DeInitialize();
}
bool COpenGL::Initialize(HWND hWnd)
{
int pfdIndex;
RECT windowRect;
this->hWnd = hWnd;
this->hDC = GetDC(hWnd);
PIXELFORMATDESCRIPTOR pfd=
{
sizeof(PIXELFORMATDESCRIPTOR), // Size Of This Pixel Format Descriptor
1, // Version Number
PFD_DRAW_TO_WINDOW | // Format Must Support Window
PFD_SUPPORT_OPENGL | // Format Must Support OpenGL
PFD_DOUBLEBUFFER, // Must Support Double Buffering
PFD_TYPE_RGBA, // Request An RGBA Format
32, // Select Our Color Depth
0, 0, 0, 0, 0, 0, // Color Bits Ignored
0, // No Alpha Buffer
0, // Shift Bit Ignored
0, // No Accumulation Buffer
0, 0, 0, 0, // Accumulation Bits Ignored
16, // 16Bit Z-Buffer (Depth Buffer)
0, // No Stencil Buffer
0, // No Auxiliary Buffer
PFD_MAIN_PLANE, // Main Drawing Layer
0, // Reserved
0, 0, 0 // Layer Masks Ignored
};
PIXELFORMATDESCRIPTOR pfdSel;
if(!(pfdIndex=ChoosePixelFormat(hDC,&pfd))) {
DeInitialize();
return false;
}
if(!SetPixelFormat(hDC,pfdIndex,&pfd)) {
2023-02-10 22:03:05 +00:00
// SetPixelFormat can only be called once per window. Vulkan WSI will do
// this automatically and fall back if it's already been set. Since Vulkan
// has similar requirements, we don't need to set it any more anyway.
// DeInitialize();
// return false;
}
if(!(hRC=wglCreateContext(hDC))) {
DeInitialize();
return false;
}
if(!wglMakeCurrent(hDC,hRC)) {
DeInitialize();
return false;
}
ogl_LoadFunctions();
LoadPBOFunctions();
wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC)wglGetProcAddress( "wglSwapIntervalEXT" );
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glEnable(GL_BLEND);
glEnable(GL_TEXTURE_2D);
glMatrixMode (GL_PROJECTION);
glLoadIdentity ();
glOrtho (0.0, 1.0, 0.0, 1.0, -1, 1);
glVertexPointer(2, GL_FLOAT, 0, vertices);
glTexCoordPointer(2, GL_FLOAT, 0, texcoords);
cgAvailable = loadCgFunctions();
if(cgAvailable) {
cgContext = cgCreateContext();
cgShader = new CGLCG(cgContext);
}
if (ShaderAvailable() && NPOTAvailable()) {
2018-05-20 17:48:38 +00:00
glslShader = new GLSLShader();
}
ApplyDisplayChanges();
glClearColor(0.0f, 0.0f, 0.0f, 0.5f);
glClear(GL_COLOR_BUFFER_BIT);
SwapBuffers(hDC);
if (ogl_GetMajorVersion() >= 3 && !Settings.AutoDisplayMessages)
{
auto defaults = S9xImGuiGetDefaults();
defaults.font_size = GUI.OSDSize;
2024-05-09 11:35:52 +00:00
defaults.spacing = static_cast<int>(defaults.font_size / 2.4);
S9xImGuiInit(&defaults);
ImGui_ImplOpenGL3_Init();
Settings.DisplayIndicators = true;
}
initDone = true;
return true;
}
void COpenGL::DeInitialize()
{
if (initDone && S9xImGuiRunning())
{
ImGui_ImplOpenGL3_Shutdown();
S9xImGuiDeinit();
}
initDone = false;
SetShaders(NULL);
DestroyDrawSurface();
wglMakeCurrent(NULL,NULL);
if(hRC) {
wglDeleteContext(hRC);
hRC = NULL;
}
if (hDC)
{
ReleaseDC(hWnd, hDC);
hDC = NULL;
}
hWnd = NULL;
afterRenderWidth = 0;
afterRenderHeight = 0;
outTextureWidth = 0;
outTextureHeight = 0;
shaderFunctionsLoaded = false;
shader_type = OGL_SHADER_NONE;
2018-05-20 17:48:38 +00:00
if (glslShader) {
delete glslShader;
glslShader = NULL;
}
if(cgShader) {
delete cgShader;
cgShader = NULL;
}
if(cgAvailable)
unloadCgLibrary();
cgAvailable = false;
}
void COpenGL::CreateDrawSurface(unsigned int width, unsigned int height)
{
HRESULT hr;
if (!NPOTAvailable()) {
unsigned int neededSize = max(width, height);
//we need at least 512 pixels (SNES_WIDTH * 2) so we can start with that value
unsigned int quadTextureSize = 512;
while (quadTextureSize < neededSize)
quadTextureSize *= 2;
width = height = quadTextureSize;
}
if(!drawTexture) {
outTextureWidth = width;
outTextureHeight = height;
glGenTextures(1,&drawTexture);
glBindTexture(GL_TEXTURE_2D,drawTexture);
glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA, outTextureWidth, outTextureHeight,0,GL_RGB,GL_UNSIGNED_SHORT_5_6_5,NULL);
if(pboFunctionsLoaded) {
glGenBuffers(1,&drawBuffer);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER,drawBuffer);
glBufferData(GL_PIXEL_UNPACK_BUFFER, outTextureWidth*outTextureHeight *2,NULL,GL_STREAM_DRAW);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER,0);
} else {
noPboBuffer = new BYTE[outTextureWidth*outTextureHeight *2];
}
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
}
}
void COpenGL::DestroyDrawSurface()
{
if(drawTexture) {
glDeleteTextures(1,&drawTexture);
drawTexture = NULL;
}
if(drawBuffer) {
glDeleteBuffers(1,&drawBuffer);
drawBuffer = NULL;
}
if(noPboBuffer) {
delete [] noPboBuffer;
noPboBuffer = NULL;
}
}
bool COpenGL::ChangeDrawSurfaceSize(unsigned int width, unsigned int height)
{
DestroyDrawSurface();
CreateDrawSurface(width, height);
SetupVertices();
return true;
}
void COpenGL::SetupVertices()
{
vertices[0] = 0.0f;
vertices[1] = 0.0f;
vertices[2] = 1.0f;
vertices[3] = 0.0f;
vertices[4] = 1.0f;
vertices[5] = 1.0f;
vertices[6] = 0.0f;
vertices[7] = 1.0f;
float tX = (float)afterRenderWidth / (float)outTextureWidth;
float tY = (float)afterRenderHeight / (float)outTextureHeight;
texcoords[0] = 0.0f;
texcoords[1] = tY;
texcoords[2] = tX;
texcoords[3] = tY;
texcoords[4] = tX;
texcoords[5] = 0.0f;
texcoords[6] = 0.0f;
texcoords[7] = 0.0f;
glTexCoordPointer(2, GL_FLOAT, 0, texcoords);
}
void wOGLViewportCallback(int source_width, int source_height,
int viewport_x, int viewport_y,
int viewport_width, int viewport_height,
int *out_dst_x, int *out_dst_y,
int *out_dst_width, int *out_dst_height)
{
/* get window size here instead of using viewport passed in - we limited the viewport before the glsl render
call already, this is simply to position smaller outputs correctly in the actual viewport
*/
RECT windowSize;
GetClientRect(GUI.hWnd, &windowSize);
RECT displayRect = CalculateDisplayRect(source_width, source_height, windowSize.right, windowSize.bottom);
*out_dst_x = displayRect.left;
*out_dst_y = displayRect.top;
*out_dst_width = displayRect.right - displayRect.left;
*out_dst_height = displayRect.bottom - displayRect.top;
}
void COpenGL::Render(SSurface Src)
{
SSurface Dst;
RECT dstRect;
unsigned int newFilterScale;
GLenum error;
if(!initDone) return;
//create a new draw surface if the filter scale changes
dstRect = GetFilterOutputSize(Src);
if(outTextureWidth != dstRect.right || outTextureHeight != dstRect.bottom)
ChangeDrawSurfaceSize(dstRect.right, dstRect.bottom);
if(pboFunctionsLoaded) {
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, drawBuffer);
Dst.Surface = (unsigned char *)glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY);
} else {
Dst.Surface = noPboBuffer;
}
Dst.Height = outTextureHeight;
Dst.Width = outTextureWidth;
Dst.Pitch = outTextureWidth * 2;
RenderMethod (Src, Dst, &dstRect);
if(pboFunctionsLoaded)
glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
if(afterRenderHeight != dstRect.bottom || afterRenderWidth != dstRect.right) {
afterRenderHeight = dstRect.bottom;
afterRenderWidth = dstRect.right;
ChangeRenderSize(0,0);
}
glBindTexture(GL_TEXTURE_2D,drawTexture);
glPixelStorei(GL_UNPACK_ROW_LENGTH, outTextureWidth);
glTexSubImage2D (GL_TEXTURE_2D,0,0,0,dstRect.right-dstRect.left,dstRect.bottom-dstRect.top,GL_RGB,GL_UNSIGNED_SHORT_5_6_5,pboFunctionsLoaded?0:noPboBuffer);
if(pboFunctionsLoaded)
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
2018-05-21 18:39:11 +00:00
RECT windowSize, displayRect;
GetClientRect(hWnd, &windowSize);
//Get maximum rect respecting AR setting
2018-06-12 22:50:24 +00:00
displayRect = CalculateDisplayRect(afterRenderWidth, afterRenderHeight, windowSize.right, windowSize.bottom);
2018-05-21 18:39:11 +00:00
// GLSL class does all the rendering, no output needed
2018-05-21 18:39:11 +00:00
if (shader_type == OGL_SHADER_GLSL) {
glslShader->render(drawTexture, afterRenderWidth, afterRenderHeight, displayRect.left, displayRect.top, displayRect.right - displayRect.left, displayRect.bottom - displayRect.top, wOGLViewportCallback);
2018-05-21 18:39:11 +00:00
}
else { // for CG shaders and old style .shader files the last pass is done here, same as no shader
2018-05-21 18:39:11 +00:00
if(shader_type == OGL_SHADER_CG) {
xySize inputSize = { (float)afterRenderWidth, (float)afterRenderHeight };
xySize xywindowSize = { (double)windowSize.right, (double)windowSize.bottom };
xySize viewportSize = { (double)(displayRect.right - displayRect.left),
(double)(displayRect.bottom - displayRect.top) };
xySize textureSize = { (double)outTextureWidth, (double)outTextureHeight };
cgShader->Render(drawTexture, textureSize, inputSize, viewportSize, xywindowSize);
}
else if (shader_type == OGL_SHADER_GLSL_OLD) {
GLint location;
float inputSize[2] = { (float)afterRenderWidth, (float)afterRenderHeight };
float outputSize[2] = { (float)(GUI.Stretch ? windowSize.right : afterRenderWidth),
(float)(GUI.Stretch ? windowSize.bottom : afterRenderHeight) };
float textureSize[2] = { (float)outTextureWidth, (float)outTextureHeight };
float frameCnt = (float)++frameCount;
location = glGetUniformLocation(shaderProgram, "rubyInputSize");
glUniform2fv(location, 1, inputSize);
location = glGetUniformLocation(shaderProgram, "rubyOutputSize");
glUniform2fv(location, 1, outputSize);
location = glGetUniformLocation(shaderProgram, "rubyTextureSize");
glUniform2fv(location, 1, textureSize);
}
2018-05-21 18:39:11 +00:00
if (Settings.BilinearFilter) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
}
else {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
}
2018-05-21 18:39:11 +00:00
glClearColor(0.0f, 0.0f, 0.0f, 0.5f);
glClear(GL_COLOR_BUFFER_BIT);
glDrawArrays(GL_QUADS, 0, 4);
}
glFlush();
if (S9xImGuiRunning())
{
ImGui_ImplOpenGL3_NewFrame();
if (S9xImGuiDraw(windowSize.right, windowSize.bottom))
{
auto* draw_data = ImGui::GetDrawData();
ImGui_ImplOpenGL3_RenderDrawData(draw_data);
}
}
WinThrottleFramerate();
SwapBuffers(hDC);
if (GUI.ReduceInputLag)
glFinish();
}
bool COpenGL::ChangeRenderSize(unsigned int newWidth, unsigned int newHeight)
{
RECT displayRect, windowSize;
if(newWidth==0||newHeight==0) {
GetClientRect(hWnd,&windowSize);
newWidth = windowSize.right;
newHeight = windowSize.bottom;
}
displayRect=CalculateDisplayRect(afterRenderWidth,afterRenderHeight,newWidth,newHeight);
glViewport(displayRect.left,newHeight-displayRect.bottom,displayRect.right-displayRect.left,displayRect.bottom-displayRect.top);
SetupVertices();
return true;
}
void COpenGL::SetSwapInterval(int frames)
{
if (wglSwapIntervalEXT)
wglSwapIntervalEXT(frames);
}
std::vector<ShaderParam>* COpenGL::GetShaderParameters(void)
2023-02-01 20:47:42 +00:00
{
if (shader_type == OGL_SHADER_GLSL && initDone)
{
// GLSLParam currently equal ShaderParam, so no conversion is neccessary
return (std::vector<ShaderParam>*)&glslShader->param;
}
2023-02-01 20:47:42 +00:00
return nullptr;
}
std::function<void(const char *)> COpenGL::GetShaderParametersSaveFunction()
{
return [&](const char *filename) {
this->glslShader->save(filename);
};
}
bool COpenGL::ApplyDisplayChanges(void)
{
if(wglSwapIntervalEXT) {
wglSwapIntervalEXT(GUI.Vsync?1:0);
}
if(GUI.shaderEnabled && GUI.OGLshaderFileName)
SetShaders(GUI.OGLshaderFileName);
else
SetShaders(NULL);
ChangeRenderSize(0,0);
return true;
}
bool COpenGL::SetFullscreen(bool fullscreen)
{
if(!initDone)
return false;
if(this->fullscreen==fullscreen)
return true;
this->fullscreen = fullscreen;
if(fullscreen) {
DEVMODE dmScreenSettings={0};
dmScreenSettings.dmSize=sizeof(dmScreenSettings);
dmScreenSettings.dmPelsWidth = GUI.FullscreenMode.width;
dmScreenSettings.dmPelsHeight = GUI.FullscreenMode.height;
dmScreenSettings.dmBitsPerPel = GUI.FullscreenMode.depth;
dmScreenSettings.dmDisplayFrequency = GUI.FullscreenMode.rate;
dmScreenSettings.dmFields=DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT|DM_DISPLAYFREQUENCY;
if(ChangeDisplaySettings(&dmScreenSettings,CDS_FULLSCREEN)!=DISP_CHANGE_SUCCESSFUL) {
this->fullscreen = false;
return false;
}
ChangeRenderSize(GUI.FullscreenMode.width,GUI.FullscreenMode.height);
} else {
ChangeDisplaySettings(NULL,0);
}
return true;
}
void COpenGL::SetSnes9xColorFormat()
{
GUI.ScreenDepth = 16;
GUI.BlueShift = 0;
GUI.GreenShift = 6;
GUI.RedShift = 11;
S9xBlit2xSaIFilterInit();
S9xBlitHQ2xFilterInit();
GUI.NeedDepthConvert = FALSE;
GUI.DepthConverted = TRUE;
return;
}
void COpenGL::EnumModes(std::vector<dMode> *modeVector)
{
DISPLAY_DEVICE dd;
dd.cb = sizeof(dd);
DWORD dev = 0;
int iMode = 0;
dMode mode;
while (EnumDisplayDevices(0, dev, &dd, 0))
{
if (!(dd.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER) && (dd.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE))
{
DEVMODE dm;
2012-01-23 17:12:47 +00:00
memset(&dm, 0, sizeof(dm));
dm.dmSize = sizeof(dm);
iMode = 0;
while(EnumDisplaySettings(dd.DeviceName,iMode,&dm)) {
if(dm.dmBitsPerPel>=16) {
mode.width = dm.dmPelsWidth;
mode.height = dm.dmPelsHeight;
mode.rate = dm.dmDisplayFrequency;
mode.depth = dm.dmBitsPerPel;
modeVector->push_back(mode);
}
iMode++;
}
}
dev++;
}
}
bool COpenGL::LoadPBOFunctions()
{
if(GUI.OGLdisablePBOs)
return false;
if(pboFunctionsLoaded)
return true;
const char *extensions = (const char *) glGetString(GL_EXTENSIONS);
if(extensions && strstr(extensions, "pixel_buffer_object")) {
if(glGenBuffers && glBindBuffer && glBufferData && glDeleteBuffers && glMapBuffer) {
pboFunctionsLoaded = true;
}
}
return pboFunctionsLoaded;
}
bool COpenGL::LoadShaderFunctions()
{
if(shaderFunctionsLoaded)
return true;
const char *extensions = (const char *) glGetString(GL_EXTENSIONS);
if(extensions && strstr(extensions, "fragment_program")) {
if(glCreateProgram &&
glCreateShader &&
glCompileShader &&
glDeleteShader &&
glDeleteProgram &&
glAttachShader &&
glDetachShader &&
glLinkProgram &&
glUseProgram &&
glShaderSource &&
glGetUniformLocation &&
glUniform2fv) {
shaderFunctionsLoaded = true;
}
}
return shaderFunctionsLoaded;
}
bool COpenGL::SetShaders(const TCHAR *file)
{
if (file && lstrcmp(file, currentShaderFile) == 0)
return true;
SetShadersCG(NULL);
SetShadersGLSL(NULL);
SetShadersGLSL_OLD(NULL);
shader_type = OGL_SHADER_NONE;
if (file) {
lstrcpy(currentShaderFile, file);
if ((lstrlen(file) > 3 && _tcsncicmp(&file[lstrlen(file) - 3], TEXT(".cg"), 3) == 0) ||
(lstrlen(file) > 4 && _tcsncicmp(&file[lstrlen(file) - 4], TEXT(".cgp"), 4) == 0)) {
return SetShadersCG(file);
}
else if ((lstrlen(file) > 7 && _tcsncicmp(&file[lstrlen(file) - 7], TEXT(".shader"), 7) == 0)) {
return SetShadersGLSL_OLD(file);
}
else {
return SetShadersGLSL(file);
}
}
*currentShaderFile = _T('\0');
return true;
}
void COpenGL::checkForCgError(const char *situation)
{
char buffer[4096];
CGerror error = cgGetError();
const char *string = cgGetErrorString(error);
if (error != CG_NO_ERROR) {
sprintf(buffer,
"Situation: %s\n"
"Error: %s\n\n"
"Cg compiler output...\n", situation, string);
MessageBoxA(0, buffer,
"Cg error", MB_OK|MB_ICONEXCLAMATION);
if (error == CG_COMPILER_ERROR) {
MessageBoxA(0, cgGetLastListing(cgContext),
"Cg compilation error", MB_OK|MB_ICONEXCLAMATION);
}
}
}
bool COpenGL::SetShadersCG(const TCHAR *file)
{
if(!cgAvailable) {
if(file)
MessageBox(NULL, TEXT("The CG runtime is unavailable, CG shaders will not run.\nConsult the snes9x readme for information on how to obtain the runtime."), TEXT("CG Error"),
MB_OK|MB_ICONEXCLAMATION);
return false;
}
if(!cgShader->LoadShader(file))
return false;
shader_type = OGL_SHADER_CG;
return true;
}
bool COpenGL::SetShadersGLSL(const TCHAR *glslFileName)
{
if (!glslShader)
return false;
glslShader->destroy();
if (!glslFileName)
return false;
2018-05-24 14:14:50 +00:00
if(!glslShader->load_shader(_tToChar(glslFileName))) {
return false;
}
2018-05-20 17:48:38 +00:00
shader_type = OGL_SHADER_GLSL;
2018-05-20 17:48:38 +00:00
return true;
}
bool COpenGL::SetShadersGLSL_OLD(const TCHAR *glslFileName)
{
char *fragment = NULL, *vertex = NULL;
IXMLDOMDocument * pXMLDoc = NULL;
IXMLDOMElement * pXDE = NULL;
IXMLDOMNode * pXDN = NULL;
HRESULT hr;
BSTR queryString, nodeContent;
TCHAR errorMsg[MAX_PATH + 50];
if (fragmentShader) {
glDetachShader(shaderProgram, fragmentShader);
glDeleteShader(fragmentShader);
fragmentShader = 0;
}
if (vertexShader) {
glDetachShader(shaderProgram, vertexShader);
glDeleteShader(vertexShader);
vertexShader = 0;
}
if (shaderProgram) {
glUseProgram(0);
glDeleteProgram(shaderProgram);
shaderProgram = 0;
}
if (glslFileName == NULL || *glslFileName == TEXT('\0'))
return true;
if (!LoadShaderFunctions()) {
MessageBox(NULL, TEXT("Unable to load OpenGL shader functions"), TEXT("Shader Loading Error"),
MB_OK | MB_ICONEXCLAMATION);
return false;
}
hr = CoCreateInstance(CLSID_DOMDocument, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pXMLDoc));
if (FAILED(hr)) {
MessageBox(NULL, TEXT("Error creating XML Parser"), TEXT("Shader Loading Error"),
MB_OK | MB_ICONEXCLAMATION);
return false;
}
VARIANT fileName;
VARIANT_BOOL ret;
fileName.vt = VT_BSTR;
#ifdef UNICODE
fileName.bstrVal = SysAllocString(glslFileName);
#else
wchar_t tempfilename[MAX_PATH];
MultiByteToWideChar(CP_UTF8, 0, glslFileName, -1, tempfilename, MAX_PATH);
fileName.bstrVal = SysAllocString(tempfilename);
#endif
hr = pXMLDoc->load(fileName, &ret);
SysFreeString(fileName.bstrVal);
if (FAILED(hr) || hr == S_FALSE) {
_stprintf(errorMsg, TEXT("Error loading GLSL shader file:\n%s"), glslFileName);
MessageBox(NULL, errorMsg, TEXT("Shader Loading Error"), MB_OK | MB_ICONEXCLAMATION);
pXMLDoc->Release();
return false;
}
VARIANT attributeValue;
BSTR attributeName;
hr = pXMLDoc->get_documentElement(&pXDE);
if (FAILED(hr) || hr == S_FALSE) {
_stprintf(errorMsg, TEXT("Error loading root element from file:\n%s"), glslFileName);
MessageBox(NULL, errorMsg, TEXT("Shader Loading Error"), MB_OK | MB_ICONEXCLAMATION);
pXMLDoc->Release();
return false;
}
attributeName = SysAllocString(L"language");
pXDE->getAttribute(attributeName, &attributeValue);
SysFreeString(attributeName);
pXDE->Release();
if (attributeValue.vt != VT_BSTR || lstrcmpiW(attributeValue.bstrVal, L"glsl")) {
_stprintf(errorMsg, TEXT("Shader language is <%s>, expected <GLSL> in file:\n%s"), attributeValue.bstrVal, glslFileName);
MessageBox(NULL, errorMsg, TEXT("Shader Loading Error"), MB_OK | MB_ICONEXCLAMATION);
if (attributeValue.vt == VT_BSTR) SysFreeString(attributeValue.bstrVal);
pXMLDoc->Release();
return false;
}
if (attributeValue.vt == VT_BSTR) SysFreeString(attributeValue.bstrVal);
queryString = SysAllocString(L"/shader/fragment");
hr = pXMLDoc->selectSingleNode(queryString, &pXDN);
SysFreeString(queryString);
if (hr == S_OK) {
hr = pXDN->get_text(&nodeContent);
if (hr == S_OK) {
int requiredChars = WideCharToMultiByte(CP_ACP, 0, nodeContent, -1, fragment, 0, NULL, NULL);
fragment = new char[requiredChars];
WideCharToMultiByte(CP_UTF8, 0, nodeContent, -1, fragment, requiredChars, NULL, NULL);
}
SysFreeString(nodeContent);
pXDN->Release();
pXDN = NULL;
}
queryString = SysAllocString(L"/shader/vertex");
hr = pXMLDoc->selectSingleNode(queryString, &pXDN);
SysFreeString(queryString);
if (hr == S_OK) {
hr = pXDN->get_text(&nodeContent);
if (hr == S_OK) {
int requiredChars = WideCharToMultiByte(CP_ACP, 0, nodeContent, -1, vertex, 0, NULL, NULL);
vertex = new char[requiredChars];
WideCharToMultiByte(CP_UTF8, 0, nodeContent, -1, vertex, requiredChars, NULL, NULL);
}
SysFreeString(nodeContent);
pXDN->Release();
pXDN = NULL;
}
pXMLDoc->Release();
if (!fragment && !vertex) {
_stprintf(errorMsg, TEXT("No vertex or fragment program in file:\n%s"), glslFileName);
MessageBox(NULL, errorMsg, TEXT("Shader Loading Error"), MB_OK | MB_ICONEXCLAMATION);
return false;
}
shaderProgram = glCreateProgram();
if (vertex) {
vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, (const GLchar **)&vertex, NULL);
glCompileShader(vertexShader);
glAttachShader(shaderProgram, vertexShader);
delete[] vertex;
}
if (fragment) {
fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, (const GLchar **)&fragment, NULL);
glCompileShader(fragmentShader);
glAttachShader(shaderProgram, fragmentShader);
delete[] fragment;
}
glLinkProgram(shaderProgram);
glUseProgram(shaderProgram);
shader_type = OGL_SHADER_GLSL_OLD;
return true;
}
bool COpenGL::ShaderAvailable()
2018-05-20 17:48:38 +00:00
{
const char *extensions = (const char *)glGetString(GL_EXTENSIONS);
2018-05-20 17:48:38 +00:00
if (!extensions)
return false;
2018-05-20 17:48:38 +00:00
if (strstr(extensions, "fragment_program") ||
strstr(extensions, "fragment_shader"))
{
return true;
}
2018-05-20 17:48:38 +00:00
return false;
}
2018-05-20 17:48:38 +00:00
bool COpenGL::NPOTAvailable()
{
const char *extensions = (const char *)glGetString(GL_EXTENSIONS);
const char *version = (const char *)glGetString(GL_VERSION);
2018-05-20 17:48:38 +00:00
if (!extensions)
return false;
2018-05-20 17:48:38 +00:00
int glVersionMajor = 0;
glVersionMajor = atoi (version);
2018-05-20 17:48:38 +00:00
if (glVersionMajor >= 2)
return true;
2018-05-20 17:48:38 +00:00
if (strstr(extensions, "non_power_of_two") ||
strstr(extensions, "npot"))
{
return true;
}
2018-05-20 17:48:38 +00:00
return false;
}