~ubuntu-branches/ubuntu/trusty/aegisub/trusty

« back to all changes in this revision

Viewing changes to src/video_out_gl.cpp

  • Committer: Package Import Robot
  • Author(s): Sebastian Reichel
  • Date: 2012-03-16 22:58:00 UTC
  • Revision ID: package-import@ubuntu.com-20120316225800-yfb8h9e5n04rk46a
Tags: upstream-2.1.9
ImportĀ upstreamĀ versionĀ 2.1.9

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Copyright (c) 2010, Thomas Goyne
 
2
// All rights reserved.
 
3
//
 
4
// Redistribution and use in source and binary forms, with or without
 
5
// modification, are permitted provided that the following conditions are met:
 
6
//
 
7
//   * Redistributions of source code must retain the above copyright notice,
 
8
//     this list of conditions and the following disclaimer.
 
9
//   * Redistributions in binary form must reproduce the above copyright notice,
 
10
//     this list of conditions and the following disclaimer in the documentation
 
11
//     and/or other materials provided with the distribution.
 
12
//   * Neither the name of the Aegisub Group nor the names of its contributors
 
13
//     may be used to endorse or promote products derived from this software
 
14
//     without specific prior written permission.
 
15
//
 
16
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 
17
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 
18
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 
19
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 
20
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 
21
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 
22
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 
23
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 
24
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 
25
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 
26
// POSSIBILITY OF SUCH DAMAGE.
 
27
//
 
28
// Aegisub Project http://www.aegisub.org/
 
29
//
 
30
// $Id: video_out_gl.cpp 4100 2010-02-14 18:09:50Z plorkyeran $
 
31
 
 
32
/// @file video_out_gl.cpp
 
33
/// @brief OpenGL based video renderer
 
34
/// @ingroup video
 
35
///
 
36
 
 
37
#include "config.h"
 
38
 
 
39
#ifndef AGI_PRE
 
40
#include <wx/log.h>
 
41
#include <algorithm>
 
42
#endif
 
43
 
 
44
using std::min;
 
45
using std::max;
 
46
 
 
47
// These must be included before local headers.
 
48
#ifdef __APPLE__
 
49
#include <OpenGL/GL.h>
 
50
#include <OpenGL/glu.h>
 
51
#else
 
52
#include <GL/gl.h>
 
53
#include <GL/glu.h>
 
54
#endif
 
55
 
 
56
#include "video_out_gl.h"
 
57
#include "utils.h"
 
58
#include "video_frame.h"
 
59
 
 
60
#define CHECK_INIT_ERROR(cmd) cmd; if (GLenum err = glGetError()) throw VideoOutInitException(_T(#cmd), err)
 
61
#define CHECK_ERROR(cmd) cmd; if (GLenum err = glGetError()) throw VideoOutRenderException(_T(#cmd), err)
 
62
 
 
63
/// @brief Structure tracking all precomputable information about a subtexture
 
64
struct VideoOutGL::TextureInfo {
 
65
        GLuint textureID;
 
66
        int dataOffset;
 
67
        int sourceH;
 
68
        int sourceW;
 
69
 
 
70
        float destX1;
 
71
        float destY1;
 
72
        float destX2;
 
73
        float destY2;
 
74
 
 
75
        float texTop;
 
76
        float texBottom;
 
77
        float texLeft;
 
78
        float texRight;
 
79
 
 
80
        TextureInfo()
 
81
                : textureID(0)
 
82
                , dataOffset(0)
 
83
                , sourceH(0)
 
84
                , sourceW(0)
 
85
                , destX1(0)
 
86
                , destY1(0)
 
87
                , destX2(0)
 
88
                , destY2(0)
 
89
                , texTop(0)
 
90
                , texBottom(1.0f)
 
91
                , texLeft(0)
 
92
                , texRight(1.0f)
 
93
        { }
 
94
};
 
95
 
 
96
/// @brief Test if a texture can be created
 
97
/// @param width The width of the texture
 
98
/// @param height The height of the texture
 
99
/// @param format The texture's format
 
100
/// @return Whether the texture could be created.
 
101
static bool TestTexture(int width, int height, GLint format) {
 
102
        glTexImage2D(GL_PROXY_TEXTURE_2D, 0, format, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
 
103
        glGetTexLevelParameteriv(GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_INTERNAL_FORMAT, &format);
 
104
        while (glGetError()) { } // Silently swallow all errors as we don't care why it failed if it did
 
105
 
 
106
        wxLogDebug(L"VideoOutGL::TestTexture: %dx%d\n", width, height);
 
107
        return format != 0;
 
108
}
 
109
 
 
110
VideoOutGL::VideoOutGL()
 
111
:       maxTextureSize(0),
 
112
        supportsRectangularTextures(false),
 
113
        supportsGlClampToEdge(false),
 
114
        internalFormat(0),
 
115
        frameWidth(0),
 
116
        frameHeight(0),
 
117
        frameFormat(0),
 
118
        frameFlipped(false),
 
119
        textureIdList(),
 
120
        textureList(),
 
121
        textureCount(0),
 
122
        textureRows(0),
 
123
        textureCols(0)
 
124
{ }
 
125
 
 
126
/// @brief Runtime detection of required OpenGL capabilities
 
127
void VideoOutGL::DetectOpenGLCapabilities() {
 
128
        if (maxTextureSize != 0) return;
 
129
 
 
130
        // Test for supported internalformats
 
131
        if (TestTexture(64, 64, GL_RGBA8)) internalFormat = GL_RGBA8;
 
132
        else if (TestTexture(64, 64, GL_RGBA)) internalFormat = GL_RGBA;
 
133
        else throw VideoOutInitException(L"Could not create a 64x64 RGB texture in any format.");
 
134
 
 
135
        // Test for the maximum supported texture size
 
136
        glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
 
137
        while (maxTextureSize > 64 && !TestTexture(maxTextureSize, maxTextureSize, internalFormat)) maxTextureSize >>= 1;
 
138
        wxLogDebug(L"VideoOutGL::DetectOpenGLCapabilities: Maximum texture size is %dx%d\n", maxTextureSize, maxTextureSize);
 
139
 
 
140
        // Test for rectangular texture support
 
141
        supportsRectangularTextures = TestTexture(maxTextureSize, maxTextureSize >> 1, internalFormat);
 
142
}
 
143
 
 
144
/// @brief If needed, create the grid of textures for displaying frames of the given format
 
145
/// @param width The frame's width
 
146
/// @param height The frame's height
 
147
/// @param format The frame's format
 
148
/// @param bpp The frame's bytes per pixel
 
149
void VideoOutGL::InitTextures(int width, int height, GLenum format, int bpp, bool flipped) {
 
150
        frameFlipped = flipped;
 
151
        // Do nothing if the frame size and format are unchanged
 
152
        if (width == frameWidth && height == frameHeight && format == frameFormat) return;
 
153
        frameWidth  = width;
 
154
        frameHeight = height;
 
155
        frameFormat = format;
 
156
        wxLogDebug(L"VideoOutGL::InitTextures: Video size: %dx%d\n", width, height);
 
157
 
 
158
        DetectOpenGLCapabilities();
 
159
 
 
160
        // Clean up old textures
 
161
        if (textureIdList.size() > 0) {
 
162
                CHECK_INIT_ERROR(glDeleteTextures(textureIdList.size(), &textureIdList[0]));
 
163
                textureIdList.clear();
 
164
                textureList.clear();
 
165
        }
 
166
 
 
167
        // Create the textures
 
168
        int textureArea = maxTextureSize - 2;
 
169
        textureRows  = (int)ceil(double(height) / textureArea);
 
170
        textureCols  = (int)ceil(double(width) / textureArea);
 
171
        textureIdList.resize(textureRows * textureCols);
 
172
        textureList.resize(textureRows * textureCols);
 
173
        CHECK_INIT_ERROR(glGenTextures(textureIdList.size(), &textureIdList[0]));
 
174
 
 
175
        /* Unfortunately, we can't simply use one of the two standard ways to do
 
176
         * tiled textures to work around texture size limits in OpenGL, due to our
 
177
         * need to support Microsoft's OpenGL emulation for RDP/VPC/video card
 
178
         * drivers that don't support OpenGL (such as the ones which Windows
 
179
         * Update pushes for ATI cards in Windows 7). GL_CLAMP_TO_EDGE requires
 
180
         * OpenGL 1.2, but the emulation only supports 1.1. GL_CLAMP + borders has
 
181
         * correct results, but takes several seconds to render each frame. As a
 
182
         * result, the code below essentially manually reimplements borders, by
 
183
         * just not using the edge when mapping the texture onto a quad. The one
 
184
         * exception to this is the texture edges which are also frame edges, as
 
185
         * there does not appear to be a trivial way to mirror the edges, and the
 
186
         * nontrivial ways are more complex that is worth to avoid a single row of
 
187
         * slightly discolored pixels along the edges at zooms over 100%.
 
188
         *
 
189
         * Given a 64x64 maximum texture size:
 
190
         *     Quads touching the top of the frame are 63 pixels tall
 
191
         *     Quads touching the bottom of the frame are up to 63 pixels tall
 
192
         *     All other quads are 62 pixels tall
 
193
         *     Quads not on the top skip the first row of the texture
 
194
         *     Quads not on the bottom skip the last row of the texture
 
195
         *     Width behaves in the same way with respect to left/right edges
 
196
         */
 
197
 
 
198
        // Calculate the position information for each texture
 
199
        int lastRow = textureRows - 1;
 
200
        int lastCol = textureCols - 1;
 
201
        for (int row = 0; row < textureRows; ++row) {
 
202
                for (int col = 0; col < textureCols; ++col) {
 
203
                        TextureInfo& ti = textureList[row * textureCols + col];
 
204
 
 
205
                        // Width and height of the area read from the frame data
 
206
                        int sourceX = col * textureArea;
 
207
                        int sourceY = row * textureArea;
 
208
                        ti.sourceW  = min(frameWidth  - sourceX, maxTextureSize);
 
209
                        ti.sourceH  = min(frameHeight - sourceY, maxTextureSize);
 
210
 
 
211
                        // Used instead of GL_PACK_SKIP_ROWS/GL_PACK_SKIP_PIXELS due to
 
212
                        // performance issues with the emulation
 
213
                        ti.dataOffset = sourceY * frameWidth * bpp + sourceX * bpp;
 
214
 
 
215
                        int textureHeight = SmallestPowerOf2(ti.sourceH);
 
216
                        int textureWidth  = SmallestPowerOf2(ti.sourceW);
 
217
                        if (!supportsRectangularTextures) {
 
218
                                textureWidth = textureHeight = max(textureWidth, textureHeight);
 
219
                        }
 
220
 
 
221
                        // Location where this texture is placed
 
222
                        // X2/Y2 will be offscreen unless the video frame happens to
 
223
                        // exactly use all of the texture
 
224
                        ti.destX1 = sourceX + (col != 0);
 
225
                        ti.destY1 = sourceY + (row != 0);
 
226
                        ti.destX2 = sourceX + textureWidth - (col != lastCol);
 
227
                        ti.destY2 = sourceY + textureHeight - (row != lastRow);
 
228
 
 
229
                        // Portion of the texture actually used
 
230
                        ti.texTop    = row == 0 ? 0 : 1.0f / textureHeight;
 
231
                        ti.texLeft   = col == 0 ? 0 : 1.0f / textureWidth;
 
232
                        ti.texBottom = row == lastRow ? 1.0f : 1.0f - 1.0f / textureHeight;
 
233
                        ti.texRight  = col == lastCol ? 1.0f : 1.0f - 1.0f / textureWidth;
 
234
 
 
235
                        ti.textureID = textureIdList[row * textureCols + col];
 
236
 
 
237
                        CreateTexture(textureWidth, textureHeight, ti, format);
 
238
                }
 
239
        }
 
240
}
 
241
 
 
242
void VideoOutGL::CreateTexture(int w, int h, const TextureInfo& ti, GLenum format) {
 
243
        CHECK_INIT_ERROR(glBindTexture(GL_TEXTURE_2D, ti.textureID));
 
244
        CHECK_INIT_ERROR(glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, w, h, 0, format, GL_UNSIGNED_BYTE, NULL));
 
245
        wxLogDebug(L"VideoOutGL::InitTextures: Using texture size: %dx%d\n", w, h);
 
246
        CHECK_INIT_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
 
247
        CHECK_INIT_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
 
248
        CHECK_INIT_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP));
 
249
        CHECK_INIT_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP));
 
250
}
 
251
 
 
252
void VideoOutGL::UploadFrameData(const AegiVideoFrame& frame) {
 
253
        if (frame.h == 0 || frame.w == 0) return;
 
254
 
 
255
        GLuint format = frame.invertChannels ? GL_BGRA_EXT : GL_RGBA;
 
256
        InitTextures(frame.w, frame.h, format, frame.GetBpp(0), frame.flipped);
 
257
 
 
258
        // Set the row length, needed to be able to upload partial rows
 
259
        CHECK_ERROR(glPixelStorei(GL_UNPACK_ROW_LENGTH, frame.w));
 
260
 
 
261
        for (unsigned i = 0; i < textureList.size(); i++) {
 
262
                TextureInfo& ti = textureList[i];
 
263
 
 
264
                CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, ti.textureID));
 
265
                CHECK_ERROR(glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, ti.sourceW,
 
266
                        ti.sourceH, format, GL_UNSIGNED_BYTE, frame.data[0] + ti.dataOffset));
 
267
        }
 
268
 
 
269
        CHECK_ERROR(glPixelStorei(GL_UNPACK_ROW_LENGTH, 0));
 
270
}
 
271
void VideoOutGL::SetViewport(int x, int y, int width, int height) {
 
272
        CHECK_ERROR(glViewport(x, y, width, height));
 
273
}
 
274
 
 
275
void VideoOutGL::Render(int sw, int sh) {
 
276
        // Clear the frame buffer
 
277
        CHECK_ERROR(glClearColor(0,0,0,0));
 
278
        CHECK_ERROR(glClearStencil(0));
 
279
        CHECK_ERROR(glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT));
 
280
 
 
281
 
 
282
        CHECK_ERROR(glShadeModel(GL_FLAT));
 
283
        CHECK_ERROR(glDisable(GL_BLEND));
 
284
 
 
285
        CHECK_ERROR(glMatrixMode(GL_PROJECTION));
 
286
        CHECK_ERROR(glLoadIdentity());
 
287
        CHECK_ERROR(glPushMatrix());
 
288
        if (frameFlipped) {
 
289
                CHECK_ERROR(glOrtho(0.0f, frameWidth, 0.0f, frameHeight, -1000.0f, 1000.0f));
 
290
        }
 
291
        else {
 
292
                CHECK_ERROR(glOrtho(0.0f, frameWidth, frameHeight, 0.0f, -1000.0f, 1000.0f));
 
293
        }
 
294
 
 
295
        // Render the current frame
 
296
        CHECK_ERROR(glEnable(GL_TEXTURE_2D));
 
297
 
 
298
        for (unsigned i = 0; i < textureList.size(); i++) {
 
299
                TextureInfo& ti = textureList[i];
 
300
 
 
301
                CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, ti.textureID));
 
302
                CHECK_ERROR(glColor4f(1.0f, 1.0f, 1.0f, 1.0f));
 
303
 
 
304
                glBegin(GL_QUADS);
 
305
                        glTexCoord2f(ti.texLeft,  ti.texTop);     glVertex2f(ti.destX1, ti.destY1);
 
306
                        glTexCoord2f(ti.texRight, ti.texTop);     glVertex2f(ti.destX2, ti.destY1);
 
307
                        glTexCoord2f(ti.texRight, ti.texBottom);  glVertex2f(ti.destX2, ti.destY2);
 
308
                        glTexCoord2f(ti.texLeft,  ti.texBottom);  glVertex2f(ti.destX1, ti.destY2);
 
309
                glEnd();
 
310
                if (GLenum err = glGetError()) throw VideoOutRenderException(L"GL_QUADS", err);
 
311
        }
 
312
        CHECK_ERROR(glDisable(GL_TEXTURE_2D));
 
313
 
 
314
        CHECK_ERROR(glPopMatrix());
 
315
        CHECK_ERROR(glOrtho(0.0f, sw, sh, 0.0f, -1000.0f, 1000.0f));
 
316
        CHECK_ERROR(glMatrixMode(GL_MODELVIEW));
 
317
        CHECK_ERROR(glLoadIdentity());
 
318
}
 
319
 
 
320
VideoOutGL::~VideoOutGL() {
 
321
        if (textureIdList.size() > 0) {
 
322
                glDeleteTextures(textureIdList.size(), &textureIdList[0]);
 
323
        }
 
324
}