/*****************************************************************************\ 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 "CGLCG.h" #include "wsnes9x.h" #include "win32_display.h" #include "snes9x.h" #include #ifndef max #define max(a, b) (((a) > (b)) ? (a) : (b)) #endif static float npot(float desired) { float out=512.0; while(outcgContext = cgContext; fboFunctionsLoaded = FALSE; ClearPasses(); LoadFBOFunctions(); frameCnt=0; } CGLCG::~CGLCG(void) { LoadShader(NULL); } void CGLCG::ClearPasses() { /* clean up cg programs, fbos and textures from all regular passes pass 0 is the orignal texture, so ignore that */ if(shaderPasses.size()>1) { for(glPassVector::iterator it=(shaderPasses.begin()+1);it!=shaderPasses.end();it++) { if(it->cgFragmentProgram) cgDestroyProgram(it->cgFragmentProgram); if(it->cgVertexProgram) cgDestroyProgram(it->cgVertexProgram); if(it->fbo) glDeleteFramebuffers(1,&it->fbo); if(it->tex) glDeleteTextures(1,&it->tex); } } for(glLutVector::iterator it=lookupTextures.begin();it!=lookupTextures.end();it++) { if(it->tex) glDeleteTextures(1,&it->tex); } for(glPrevDeque::iterator it=prevPasses.begin();it!=prevPasses.end();it++) { if(it->tex) glDeleteTextures(1,&it->tex); } shaderPasses.clear(); lookupTextures.clear(); prevPasses.clear(); // prevPasses deque is always filled with PREV + PREV1-6 elements prevPasses.resize(7); shaderLoaded = false; } bool CGLCG::LoadFBOFunctions() { if(fboFunctionsLoaded) return true; const char *extensions = (const char *) glGetString(GL_EXTENSIONS); if(extensions && strstr(extensions, "framebuffer_object")) { if(glGenFramebuffers && glDeleteFramebuffers && glBindFramebuffer && glFramebufferTexture2D && glClientActiveTexture) { fboFunctionsLoaded = true; } } return fboFunctionsLoaded; } void CGLCG::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 CGLCG::LoadShader(const TCHAR *shaderFile) { CCGShader cgShader; TCHAR shaderPath[MAX_PATH]; TCHAR tempPath[MAX_PATH]; CGprofile vertexProfile, fragmentProfile; GLenum error; if(!fboFunctionsLoaded) { MessageBox(NULL, TEXT("Your OpenGL graphics driver does not support framebuffer objects.\nYou will not be able to use CG shaders in OpenGL mode."), TEXT("CG Error"), MB_OK|MB_ICONEXCLAMATION); return false; } vertexProfile = cgGLGetLatestProfile(CG_GL_VERTEX); fragmentProfile = cgGLGetLatestProfile(CG_GL_FRAGMENT); cgGLDisableProfile(vertexProfile); cgGLDisableProfile(fragmentProfile); ClearPasses(); if (shaderFile == NULL || *shaderFile==TEXT('\0')) return true; lstrcpy(shaderPath, shaderFile); ReduceToPath(shaderPath); SetCurrentDirectory(shaderPath); if(!cgShader.LoadShader(_tToChar(shaderFile))) return false; cgGLSetOptimalOptions(vertexProfile); cgGLSetOptimalOptions(fragmentProfile); /* insert dummy pass that will contain the original texture */ shaderPasses.push_back(shaderPass()); for(CCGShader::passVector::iterator it=cgShader.shaderPasses.begin(); it!=cgShader.shaderPasses.end();it++) { shaderPass pass; pass.scaleParams = it->scaleParams; /* if this is the last pass (the only one that can have CG_SCALE_NONE) and no filter has been set use the GUI setting */ if(pass.scaleParams.scaleTypeX==CG_SCALE_NONE && !it->filterSet) { pass.linearFilter = Settings.BilinearFilter; } else { pass.linearFilter = it->linearFilter; } pass.frameCounterMod = it->frameCounterMod; pass.floatFbo = it->floatFbo; // paths in the meta file can be relative _tfullpath(tempPath,_tFromChar(it->cgShaderFile),MAX_PATH); char *fileContents = ReadShaderFileContents(tempPath); if(!fileContents) return false; // individual shader might include files, these should be relative to shader ReduceToPath(tempPath); SetCurrentDirectory(tempPath); pass.cgVertexProgram = cgCreateProgram( cgContext, CG_SOURCE, fileContents, vertexProfile, "main_vertex", NULL); checkForCgError("Compiling vertex program"); pass.cgFragmentProgram = cgCreateProgram( cgContext, CG_SOURCE, fileContents, fragmentProfile, "main_fragment", NULL); checkForCgError("Compiling fragment program"); // set path back for next pass SetCurrentDirectory(shaderPath); delete [] fileContents; if(!pass.cgVertexProgram || !pass.cgFragmentProgram) { return false; } cgGLLoadProgram(pass.cgVertexProgram); cgGLLoadProgram(pass.cgFragmentProgram); /* generate framebuffer and texture for this pass and apply default texture settings */ glGenFramebuffers(1,&pass.fbo); glGenTextures(1,&pass.tex); glBindTexture(GL_TEXTURE_2D,pass.tex); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); shaderPasses.push_back(pass); } for(std::vector::iterator it=cgShader.lookupTextures.begin();it!=cgShader.lookupTextures.end();it++) { lookupTexture tex; strcpy(tex.id,it->id); /* generate texture for the lut and apply specified filter setting */ glGenTextures(1,&tex.tex); glBindTexture(GL_TEXTURE_2D,tex.tex); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, it->linearfilter?GL_LINEAR:GL_NEAREST); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, it->linearfilter?GL_LINEAR:GL_NEAREST); _tfullpath(tempPath,_tFromChar(it->texturePath),MAX_PATH); // simple file extension png/tga decision int strLen = strlen(it->texturePath); if(strLen>4) { if(!strcasecmp(&it->texturePath[strLen-4],".png")) { int width, height; bool hasAlpha; GLubyte *texData; if(loadPngImage(tempPath,width,height,hasAlpha,&texData)) { glPixelStorei(GL_UNPACK_ROW_LENGTH, width); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, hasAlpha ? GL_RGBA : GL_RGB, GL_UNSIGNED_BYTE, texData); free(texData); } } else if(!strcasecmp(&it->texturePath[strLen-4],".tga")) { STGA stga; if(loadTGA(tempPath,stga)) { glPixelStorei(GL_UNPACK_ROW_LENGTH, stga.width); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, stga.width, stga.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, stga.data); } } } lookupTextures.push_back(tex); } /* enable texture unit 1 for the lookup textures */ glClientActiveTexture(GL_TEXTURE1); glEnableClientState(GL_TEXTURE_COORD_ARRAY); glTexCoordPointer(2,GL_FLOAT,0,lut_coords); glClientActiveTexture(GL_TEXTURE0); /* generate textures and set default values for the pref-filled PREV deque. */ for(int i=0;i0) { float prev_videoSize[2] = { (float)prevPasses[0].videoSize.x, (float)prevPasses[0].videoSize.y }; float prev_textureSize[2] = { (float)prevPasses[0].textureSize.x, (float)prevPasses[0].textureSize.y }; setProgram2fv(pass,"PREV.video_size",prev_videoSize); setProgram2fv(pass,"PREV.texture_size",prev_textureSize); setTextureParameter(pass,"PREV.texture",prevPasses[0].tex); setTexCoordsParameter(pass,"PREV.tex_coord",prevPasses[0].texCoords); } /* PREV1-6 parameters */ for(int i=1;i2) { for(int i=1;i