From 3b9afa126e7055ef59dbd05bf7dd14892801b364 Mon Sep 17 00:00:00 2001 From: Rodrigo Braz Monteiro Date: Sat, 27 Jan 2007 06:15:25 +0000 Subject: [PATCH] Mostly operational pixel shaded YV12->RGB32 conversion. (needs glew for now) Originally committed to SVN as r899. --- aegisub/gl_wrap.cpp | 158 +++++++++++++++++++++++++++++++ aegisub/gl_wrap.h | 10 ++ aegisub/options.cpp | 1 + aegisub/stdwx.h | 3 + aegisub/video_context.cpp | 25 ++++- aegisub/video_context.h | 2 + aegisub/video_display.cpp | 9 +- aegisub/video_display_visual.cpp | 67 ++++++++----- aegisub/video_provider_avs.cpp | 7 +- aegisub/video_provider_dshow.cpp | 7 +- 10 files changed, 252 insertions(+), 37 deletions(-) diff --git a/aegisub/gl_wrap.cpp b/aegisub/gl_wrap.cpp index c03f69f5a..f1e746564 100644 --- a/aegisub/gl_wrap.cpp +++ b/aegisub/gl_wrap.cpp @@ -36,10 +36,18 @@ /////////// // Headers +#include #include #include "gl_wrap.h" +//////////////// +// GLEW library +#if __VISUALC__ >= 1200 +#pragma comment(lib,"glew32.lib") +#endif + + ///////////// // Draw line void OpenGLWrapper::DrawLine(float x1,float y1,float x2,float y2) { @@ -222,3 +230,153 @@ void OpenGLWrapper::SetModeFill() { glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); } } + + +////////////////////////// +// Are shaders available? +bool OpenGLWrapper::ShadersAvailable() { + if (GLEW_VERSION_2_0) return true; + return false; +} + + +/////////////////// +// Initialize GLEW +void OpenGLWrapper::InitializeGLEW() { + static bool initialized = false; + if (!initialized) { + initialized = true; + glewInit(); + } +} + + +////////////////////// +// Set current shader +void OpenGLWrapper::SetShader(GLuint i) { + InitializeGLEW(); + glUseProgram(i); +} + + +////////////////////////// +// Destroy shader program +void OpenGLWrapper::DestroyShaderProgram(GLuint i) { + InitializeGLEW(); + SetShader(0); + glDeleteProgram(i); +} + + +//////////////////////////////////////////////////////// +// Create shader program from vertex and pixel shaders +GLuint OpenGLWrapper::CreateShaderProgram(GLuint vertex,GLuint pixel) { + // Create instance + InitializeGLEW(); + GLuint program = glCreateProgram(); + + // Attach shaders + glAttachShader(program,vertex); + glAttachShader(program,pixel); + + // Link + glLinkProgram(program); + + // Return + return program; +} + + +///////////////////////////////// +// Create standard Vertex shader +GLuint OpenGLWrapper::CreateStandardVertexShader() { + // Create instance + InitializeGLEW(); + GLuint shader = glCreateShader(GL_VERTEX_SHADER); + + // Read source + char source[] = + "void main() {\n" + " gl_TexCoord[0] = gl_MultiTexCoord0;\n" + " gl_Position = ftransform();\n" + "}"; + const GLchar *src = source; + glShaderSource(shader,1,&src,NULL); + + // Compile + glCompileShader(shader); + + // Return + return shader; +} + + +/////////////////////////////////// +// Create YV12->RGB32 Pixel Shader +GLuint OpenGLWrapper::CreateYV12PixelShader() { + // Create instance + InitializeGLEW(); + GLuint shader = glCreateShader(GL_FRAGMENT_SHADER); + + // Read source + char source[] = + "uniform sampler2D tex;\n" + "uniform vec2 off1;\n" + "uniform vec2 off2;\n" + "\n" + "void main() {\n" + " vec2 pos = gl_TexCoord[0].st;\n" + " vec4 y_bias = vec4(-0.063,-0.063,-0.063,0.0);\n" + " vec4 y_mult = vec4(1.164,1.164,1.164,1.0);\n" + " vec4 color_y = (texture2D(tex,pos) + y_bias) * y_mult;\n" + " pos *= 0.5;\n" + " vec4 uv_bias = vec4(-0.5,-0.5,-0.5,0.0);\n" + " vec4 uv_mult = vec4(0.0,-0.391,2.018,1.0);\n" + //" vec4 uv_mult = vec4(0.0,-0.344,1.770,1.0);\n" + " vec4 color_u = (texture2D(tex,pos + off1) + uv_bias) * uv_mult;\n" + " uv_mult = vec4(1.596,-0.813,0.0,1.0);\n" + //" uv_mult = vec4(1.403,-0.714,0.0,1.0);\n" + " vec4 color_v = (texture2D(tex,pos + off2) + uv_bias) * uv_mult;\n" + " gl_FragColor = color_y + color_u + color_v;\n" + "}"; + const GLchar *src = source; + glShaderSource(shader,1,&src,NULL); + + // Compile + glCompileShader(shader); + + // Return + return shader; +} + + +///////////////////////////////////// +// Create YV12->RGB32 Shader Program +GLuint OpenGLWrapper::CreateYV12Shader(float tw,float th) { + // Create vertex shader + GLuint ver = OpenGLWrapper::CreateStandardVertexShader(); + if (glGetError() != 0) throw _T("Error creating generic vertex shader"); + + // Create pixel shader + GLuint pix = OpenGLWrapper::CreateYV12PixelShader(); + if (glGetError() != 0) throw _T("Error creating YV12 pixel shader"); + + // Create program + GLuint program = OpenGLWrapper::CreateShaderProgram(ver,pix); + if (glGetError() != 0) throw _T("Error creating shader program"); + + // Set shader + OpenGLWrapper::SetShader(program); + if (glGetError() != 0) throw _T("Error setting shader"); + + // Set uniform variables + GLuint address = glGetUniformLocation(program,"tex"); + glUniform1i(address, 0); + address = glGetUniformLocation(program,"off1"); + glUniform2f(address, 0.0f, th); + address = glGetUniformLocation(program,"off2"); + glUniform2f(address, tw*0.5f, th); + + // Return shader + return program; +} diff --git a/aegisub/gl_wrap.h b/aegisub/gl_wrap.h index 8ebd1610d..5fb76890f 100644 --- a/aegisub/gl_wrap.h +++ b/aegisub/gl_wrap.h @@ -45,6 +45,11 @@ private: float r2,g2,b2,a2; int lw; + static void InitializeGLEW(); + static GLuint CreateStandardVertexShader(); + static GLuint CreateYV12PixelShader(); + static GLuint CreateShaderProgram(GLuint vertex,GLuint pixel); + public: void SetLineColour(wxColour col,float alpha=1.0f,int width=1); void SetFillColour(wxColour col,float alpha=1.0f); @@ -55,4 +60,9 @@ public: void DrawCircle(float x,float y,float radius) { DrawEllipse(x,y,radius,radius); } void DrawRectangle(float x1,float y1,float x2,float y2); void DrawRing(float x,float y,float r1,float r2,float ar=1.0f,float arcStart=0.0f,float arcEnd=0.0f); + + static bool ShadersAvailable(); + static void SetShader(GLuint i); + static void DestroyShaderProgram(GLuint i); + static GLuint CreateYV12Shader(float tw,float th); }; diff --git a/aegisub/options.cpp b/aegisub/options.cpp index 629aa2ae9..2d26ce405 100644 --- a/aegisub/options.cpp +++ b/aegisub/options.cpp @@ -148,6 +148,7 @@ void OptionsManager::LoadDefaults() { SetBool(_T("Allow Ancient Avisynth"),false); SetText(_T("Video Provider"),_T("Avisynth")); SetText(_T("Subtitles Provider"),_T("CSRI")); + SetBool(_T("Video Use Pixel Shaders"),false); // Audio Options SetBool(_T("Audio Next Line on Commit"),true); diff --git a/aegisub/stdwx.h b/aegisub/stdwx.h index a1c10765b..94857517a 100644 --- a/aegisub/stdwx.h +++ b/aegisub/stdwx.h @@ -78,6 +78,9 @@ #include #include #include +#include +#include +#include #include diff --git a/aegisub/video_context.cpp b/aegisub/video_context.cpp index 163773f2c..6bdbd4edd 100644 --- a/aegisub/video_context.cpp +++ b/aegisub/video_context.cpp @@ -65,6 +65,7 @@ #include "video_slider.h" #include "video_box.h" #include "utils.h" +#include "gl_wrap.h" /////// @@ -93,6 +94,7 @@ VideoContext::VideoContext() { glContext = NULL; lastTex = 0; lastFrame = -1; + yv12shader = 0; // Set options audio = NULL; @@ -142,6 +144,12 @@ void VideoContext::Clear() { ///////// // Reset void VideoContext::Reset() { + // Reset shader + if (yv12shader) { + OpenGLWrapper::DestroyShaderProgram(yv12shader); + yv12shader = 0; + } + // Clear keyframes KeyFrames.Clear(); keyFramesLoaded = false; @@ -426,6 +434,7 @@ GLuint VideoContext::GetFrameAsTexture(int n) { glShadeModel(GL_FLAT); // Generate texture with GL + //glActiveTexture(GL_TEXTURE0); glGenTextures(1, &lastTex); if (glGetError() != 0) throw _T("Error generating texture."); glBindTexture(GL_TEXTURE_2D, lastTex); @@ -439,9 +448,9 @@ GLuint VideoContext::GetFrameAsTexture(int n) { // Load image data into texture int height = frame.h; - if (frame.format == FORMAT_YV12) height = frame.h * 3 / 2; + if (frame.format == FORMAT_YV12) height = height * 3 / 2; int tw = SmallestPowerOf2(frame.w); - int th = SmallestPowerOf2(frame.h); + int th = SmallestPowerOf2(height); texW = float(frame.w)/float(tw); texH = float(frame.h)/float(th); glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA8,tw,th,0,format,GL_UNSIGNED_BYTE,NULL); @@ -454,6 +463,11 @@ GLuint VideoContext::GetFrameAsTexture(int n) { // Set priority float priority = 1.0f; glPrioritizeTextures(1,&lastTex,&priority); + + // Create shader if necessary + if (frame.format == FORMAT_YV12 && yv12shader == 0) { + yv12shader = OpenGLWrapper::CreateYV12Shader(texW,texH); + } } // Load texture data @@ -730,3 +744,10 @@ void VideoContext::SetAspectRatio(int _type, double value) { arValue = value; UpdateDisplays(true); } + + +//////////////////////////// +// Enable or disable shader +void VideoContext::SetShader(bool enabled) { + OpenGLWrapper::SetShader(enabled ? yv12shader : 0); +} diff --git a/aegisub/video_context.h b/aegisub/video_context.h index 356d4b8ec..00f5d7c32 100644 --- a/aegisub/video_context.h +++ b/aegisub/video_context.h @@ -67,6 +67,7 @@ private: std::list displayList; GLuint lastTex; + GLuint yv12shader; int lastFrame; wxGLContext *glContext; VideoFrameFormat vidFormat; @@ -130,6 +131,7 @@ public: float GetTexW() { return texW; } float GetTexH() { return texH; } VideoFrameFormat GetFormat() { return vidFormat; } + void SetShader(bool enabled); bool IsLoaded() { return loaded; } bool IsPlaying() { return isPlaying; } diff --git a/aegisub/video_display.cpp b/aegisub/video_display.cpp index 7bf07bd93..1ad8b7cf6 100644 --- a/aegisub/video_display.cpp +++ b/aegisub/video_display.cpp @@ -203,8 +203,9 @@ void VideoDisplay::Render() { float left = 0.0; float right = context->GetTexW(); - // Draw interleaved frame or luma of YV12 + // Draw frame glDisable(GL_BLEND); + context->SetShader(true); glBindTexture(GL_TEXTURE_2D, VideoContext::Get()->GetFrameAsTexture(-1)); glColor4f(1.0f,1.0f,1.0f,1.0f); glBegin(GL_QUADS); @@ -221,11 +222,7 @@ void VideoDisplay::Render() { glTexCoord2f(right,top); glVertex2f(sw,0); glEnd(); - - // Draw UV planes - if (context->GetFormat() == FORMAT_YV12) { - - } + context->SetShader(false); // Draw overlay visual->DrawOverlay(); diff --git a/aegisub/video_display_visual.cpp b/aegisub/video_display_visual.cpp index 43f813ef1..afabd0afd 100644 --- a/aegisub/video_display_visual.cpp +++ b/aegisub/video_display_visual.cpp @@ -188,6 +188,16 @@ void VideoDisplayVisual::DrawOverlay() { } else GetLineScale(diag,scalX,scalY); + // Get angle + GetLineRotation(diag,rx,ry,rz); + if (isCur) { + if (mode == 2) rz = curAngle; + else if (mode == 3) { + rx = curAngle; + ry = curAngle2; + } + } + // Mouse over? if (diag == diagHigh) { high = true; @@ -225,16 +235,6 @@ void VideoDisplayVisual::DrawOverlay() { DrawLine(dx,dy-16,dx,dy+16); DrawLine(dx-16,dy,dx+16,dy); - // Get angle - GetLineRotation(diag,rx,ry,rz); - if (isCur) { - if (mode == 2) rz = curAngle; - else { - rx = curAngle; - ry = curAngle2; - } - } - // Rotate Z if (mode == 2) { // Transform @@ -387,37 +387,54 @@ void VideoDisplayVisual::DrawOverlay() { // Scale if (mode == 4) { - // Scale parameters + // Set dx/dy int len = 160; - int lenx = int(1.6 * scalX); - int leny = int(1.6 * scalY); dx = MID(len/2+10,dx,sw-len/2-30); dy = MID(len/2+10,dy,sh-len/2-30); - int drawX = dx + len/2 + 10; - int drawY = dy + len/2 + 10; + + // Transform grid + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + glTranslatef(dx,dy,0.0f); + float matrix[16] = { 2500, 0, 0, 0, 0, 2500, 0, 0, 0, 0, 1, 1, 0, 0, 2500, 2500 }; + glMultMatrixf(matrix); + glScalef(1.0f,1.0f,8.0f); + if (ry != 0.0f) glRotatef(ry,0.0f,-1.0f,0.0f); + if (rx != 0.0f) glRotatef(rx,-1.0f,0.0f,0.0f); + if (rz != 0.0f) glRotatef(rz,0.0f,0.0f,-1.0f); + + // Scale parameters + int lenx = int(1.6 * scalX); + int leny = int(1.6 * scalY); + int drawX = len/2 + 10; + int drawY = len/2 + 10; // Draw length markers SetLineColour(colour[3],1.0f,2); - DrawLine(dx-lenx/2,drawY+10,dx+lenx/2,drawY+10); - DrawLine(drawX+10,dy-leny/2,drawX+10,dy+leny/2); + DrawLine(-lenx/2,drawY+10,lenx/2,drawY+10); + DrawLine(drawX+10,-leny/2,drawX+10,leny/2); SetLineColour(colour[0],1.0f,1); SetFillColour(colour[brushCol],0.3f); - DrawCircle(dx+lenx/2,drawY+10,4); - DrawCircle(drawX+10,dy-leny/2,4); + DrawCircle(lenx/2,drawY+10,4); + DrawCircle(drawX+10,-leny/2,4); // Draw horizontal scale SetLineColour(colour[0],1.0f,1); - DrawRectangle(dx-len/2,drawY,dx+len/2+1,drawY+5); + DrawRectangle(-len/2,drawY,len/2+1,drawY+5); SetLineColour(colour[0],1.0f,2); - DrawLine(dx-len/2+1,drawY+5,dx-len/2+1,drawY+15); - DrawLine(dx+len/2,drawY+5,dx+len/2,drawY+15); + DrawLine(-len/2+1,drawY+5,-len/2+1,drawY+15); + DrawLine(len/2,drawY+5,len/2,drawY+15); // Draw vertical scale SetLineColour(colour[0],1.0f,1); - DrawRectangle(drawX,dy-len/2,drawX+5,dy+len/2+1); + DrawRectangle(drawX,-len/2,drawX+5,len/2+1); SetLineColour(colour[0],1.0f,2); - DrawLine(drawX+5,dy-len/2+1,drawX+15,dy-len/2+1); - DrawLine(drawX+5,dy+len/2,drawX+15,dy+len/2); + DrawLine(drawX+5,-len/2+1,drawX+15,-len/2+1); + DrawLine(drawX+5,len/2,drawX+15,len/2); + + // Restore gl's state + glPopMatrix(); } // Clip diff --git a/aegisub/video_provider_avs.cpp b/aegisub/video_provider_avs.cpp index 576ff417d..e06dcfb57 100644 --- a/aegisub/video_provider_avs.cpp +++ b/aegisub/video_provider_avs.cpp @@ -49,6 +49,7 @@ #include "main.h" #include "vfr.h" #include "ass_file.h" +#include "gl_wrap.h" //////////// @@ -293,8 +294,10 @@ PClip AvisynthVideoProvider::OpenVideo(wxString _filename, bool mpeg2dec3_priori } // Convert to RGB32 - script = env->Invoke("ConvertToRGB32", script); - AVSTRACE(_T("AvisynthVideoProvider::OpenVideo: Converted to RGB32")); + if (OpenGLWrapper::ShadersAvailable() && !Options.AsBool(_T("Video Use Pixel Shaders"))) { + script = env->Invoke("ConvertToRGB32", script); + AVSTRACE(_T("AvisynthVideoProvider::OpenVideo: Converted to RGB32")); + } // Directshow //if (usedDirectshow) wxMessageBox(_T("Warning! The file is being opened using Avisynth's DirectShowSource, which has unreliable seeking. Frame numbers might not match the real number. PROCEED AT YOUR OWN RISK!"),_T("DirectShowSource warning"),wxICON_EXCLAMATION); diff --git a/aegisub/video_provider_dshow.cpp b/aegisub/video_provider_dshow.cpp index 1ddf3d582..b912e3c6b 100644 --- a/aegisub/video_provider_dshow.cpp +++ b/aegisub/video_provider_dshow.cpp @@ -52,6 +52,8 @@ #include "utils.h" #include "vfr.h" #include "videosink.h" +#include "gl_wrap.h" +#include "options.h" /////////////////////// @@ -292,8 +294,9 @@ HRESULT DirectShowVideoProvider::OpenVideo(wxString _filename) { if (!sink2) return E_NOINTERFACE; // Set allowed types for sink - //sink->SetAllowedTypes(IVS_RGB32|IVS_YV12|IVS_YUY2); - sink->SetAllowedTypes(IVS_RGB24); + unsigned int types = IVS_RGB24 | IVS_RGB32; + if (OpenGLWrapper::ShadersAvailable() && !Options.AsBool(_T("Video Use Pixel Shaders"))) types = types | IVS_YV12; + sink->SetAllowedTypes(types); // Pass the event to sink, so it gets set when a frame is available ResetEvent(m_hFrameReady);