Rewrite the texture grid positioning code again, fixing several visual defects that showed up in OpenGL 1.1 mode. Updates #1153.
Originally committed to SVN as r4097.
This commit is contained in:
parent
c9ba8329ad
commit
175b2a4648
1 changed files with 84 additions and 96 deletions
|
@ -1,4 +1,4 @@
|
||||||
// Copyright (c) 2009, Thomas Goyne
|
// Copyright (c) 2010, Thomas Goyne
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
//
|
//
|
||||||
// Redistribution and use in source and binary forms, with or without
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
@ -53,19 +53,12 @@
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "video_frame.h"
|
#include "video_frame.h"
|
||||||
|
|
||||||
// Windows only has headers for OpenGL 1.1 and GL_CLAMP_TO_EDGE is 1.2
|
|
||||||
#ifndef GL_CLAMP_TO_EDGE
|
|
||||||
#define GL_CLAMP_TO_EDGE 0x812F
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define CHECK_INIT_ERROR(cmd) cmd; if (GLenum err = glGetError()) throw VideoOutInitException(_T(#cmd), err)
|
#define CHECK_INIT_ERROR(cmd) cmd; if (GLenum err = glGetError()) throw VideoOutInitException(_T(#cmd), err)
|
||||||
#define CHECK_ERROR(cmd) cmd; if (GLenum err = glGetError()) throw VideoOutRenderException(_T(#cmd), err)
|
#define CHECK_ERROR(cmd) cmd; if (GLenum err = glGetError()) throw VideoOutRenderException(_T(#cmd), err)
|
||||||
|
|
||||||
/// @brief Structure tracking all precomputable information about a subtexture
|
/// @brief Structure tracking all precomputable information about a subtexture
|
||||||
struct VideoOutGL::TextureInfo {
|
struct VideoOutGL::TextureInfo {
|
||||||
/// The OpenGL texture id this is for
|
|
||||||
GLuint textureID;
|
GLuint textureID;
|
||||||
/// The byte offset into the frame's data block
|
|
||||||
int dataOffset;
|
int dataOffset;
|
||||||
int sourceH;
|
int sourceH;
|
||||||
int sourceW;
|
int sourceW;
|
||||||
|
@ -106,7 +99,7 @@ static bool TestTexture(int width, int height, GLint format) {
|
||||||
glGetTexLevelParameteriv(GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_INTERNAL_FORMAT, &format);
|
glGetTexLevelParameteriv(GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_INTERNAL_FORMAT, &format);
|
||||||
while (glGetError()) { } // Silently swallow all errors as we don't care why it failed if it did
|
while (glGetError()) { } // Silently swallow all errors as we don't care why it failed if it did
|
||||||
|
|
||||||
wxLogDebug("VideoOutGL::TestTexture: %dx%d\n", width, height);
|
wxLogDebug(L"VideoOutGL::TestTexture: %dx%d\n", width, height);
|
||||||
return format != 0;
|
return format != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,22 +131,10 @@ void VideoOutGL::DetectOpenGLCapabilities() {
|
||||||
// Test for the maximum supported texture size
|
// Test for the maximum supported texture size
|
||||||
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
|
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
|
||||||
while (maxTextureSize > 64 && !TestTexture(maxTextureSize, maxTextureSize, internalFormat)) maxTextureSize >>= 1;
|
while (maxTextureSize > 64 && !TestTexture(maxTextureSize, maxTextureSize, internalFormat)) maxTextureSize >>= 1;
|
||||||
wxLogDebug("VideoOutGL::DetectOpenGLCapabilities: Maximum texture size is %dx%d\n", maxTextureSize, maxTextureSize);
|
wxLogDebug(L"VideoOutGL::DetectOpenGLCapabilities: Maximum texture size is %dx%d\n", maxTextureSize, maxTextureSize);
|
||||||
|
|
||||||
// Test for rectangular texture support
|
// Test for rectangular texture support
|
||||||
supportsRectangularTextures = TestTexture(maxTextureSize, maxTextureSize >> 1, internalFormat);
|
supportsRectangularTextures = TestTexture(maxTextureSize, maxTextureSize >> 1, internalFormat);
|
||||||
|
|
||||||
// Test GL_CLAMP_TO_EDGE support
|
|
||||||
glTexImage2D(GL_PROXY_TEXTURE_2D, 0, GL_RGBA8, 64, 64, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
||||||
if (glGetError()) {
|
|
||||||
supportsGlClampToEdge = false;
|
|
||||||
wxLogDebug("VideoOutGL::DetectOpenGLCapabilities: Using GL_CLAMP\n");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
supportsGlClampToEdge = true;
|
|
||||||
wxLogDebug("VideoOutGL::DetectOpenGLCapabilities: Using GL_CLAMP_TO_EDGE\n");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief If needed, create the grid of textures for displaying frames of the given format
|
/// @brief If needed, create the grid of textures for displaying frames of the given format
|
||||||
|
@ -162,13 +143,13 @@ void VideoOutGL::DetectOpenGLCapabilities() {
|
||||||
/// @param format The frame's format
|
/// @param format The frame's format
|
||||||
/// @param bpp The frame's bytes per pixel
|
/// @param bpp The frame's bytes per pixel
|
||||||
void VideoOutGL::InitTextures(int width, int height, GLenum format, int bpp, bool flipped) {
|
void VideoOutGL::InitTextures(int width, int height, GLenum format, int bpp, bool flipped) {
|
||||||
// Do nothing if the frame size and format are unchanged
|
|
||||||
if (width == frameWidth && height == frameHeight && format == frameFormat && flipped == frameFlipped) return;
|
|
||||||
frameWidth = width;
|
|
||||||
frameHeight = height;
|
|
||||||
frameFormat = format;
|
|
||||||
frameFlipped = flipped;
|
frameFlipped = flipped;
|
||||||
wxLogDebug("VideoOutGL::InitTextures: Video size: %dx%d\n", width, height);
|
// Do nothing if the frame size and format are unchanged
|
||||||
|
if (width == frameWidth && height == frameHeight && format == frameFormat) return;
|
||||||
|
frameWidth = width;
|
||||||
|
frameHeight = height;
|
||||||
|
frameFormat = format;
|
||||||
|
wxLogDebug(L"VideoOutGL::InitTextures: Video size: %dx%d\n", width, height);
|
||||||
|
|
||||||
DetectOpenGLCapabilities();
|
DetectOpenGLCapabilities();
|
||||||
|
|
||||||
|
@ -180,89 +161,88 @@ void VideoOutGL::InitTextures(int width, int height, GLenum format, int bpp, boo
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the textures
|
// Create the textures
|
||||||
textureRows = (int)ceil(double(height) / maxTextureSize);
|
int textureArea = maxTextureSize - 2;
|
||||||
textureCols = (int)ceil(double(width) / maxTextureSize);
|
textureRows = (int)ceil(double(height) / textureArea);
|
||||||
|
textureCols = (int)ceil(double(width) / textureArea);
|
||||||
textureIdList.resize(textureRows * textureCols);
|
textureIdList.resize(textureRows * textureCols);
|
||||||
textureList.resize(textureRows * textureCols);
|
textureList.resize(textureRows * textureCols);
|
||||||
CHECK_INIT_ERROR(glGenTextures(textureIdList.size(), &textureIdList[0]));
|
CHECK_INIT_ERROR(glGenTextures(textureIdList.size(), &textureIdList[0]));
|
||||||
|
|
||||||
|
/* Unfortunately, we can't simply use one of the two standard ways to do
|
||||||
|
* tiled textures to work around texture size limits in OpenGL, due to our
|
||||||
|
* need to support Microsoft's OpenGL emulation for RDP/VPC/video card
|
||||||
|
* drivers that don't support OpenGL (such as the ones which Windows
|
||||||
|
* Update pushes for ATI cards in Windows 7). GL_CLAMP_TO_EDGE requires
|
||||||
|
* OpenGL 1.2, but the emulation only supports 1.1. GL_CLAMP + borders has
|
||||||
|
* correct results, but takes several seconds to render each frame. As a
|
||||||
|
* result, the code below essentially manually reimplements borders, by
|
||||||
|
* just not using the edge when mapping the texture onto a quad. The one
|
||||||
|
* exception to this is the texture edges which are also frame edges, as
|
||||||
|
* there does not appear to be a trivial way to mirror the edges, and the
|
||||||
|
* nontrivial ways are more complex that is worth to avoid a single row of
|
||||||
|
* slightly discolored pixels along the edges at zooms over 100%.
|
||||||
|
*
|
||||||
|
* Given a 64x64 maximum texture size:
|
||||||
|
* Quads touching the top of the frame are 63 pixels tall
|
||||||
|
* Quads touching the bottom of the frame are up to 63 pixels tall
|
||||||
|
* All other quads are 62 pixels tall
|
||||||
|
* Quads not on the top skip the first row of the texture
|
||||||
|
* Quads not on the bottom skip the last row of the texture
|
||||||
|
* Width behaves in the same way with respect to left/right edges
|
||||||
|
*/
|
||||||
|
|
||||||
// Calculate the position information for each texture
|
// Calculate the position information for each texture
|
||||||
int sourceY = 0;
|
int lastRow = textureRows - 1;
|
||||||
float destY = -1.0f;
|
int lastCol = textureCols - 1;
|
||||||
for (int i = 0; i < textureRows; i++) {
|
for (int row = 0; row < textureRows; ++row) {
|
||||||
int sourceX = 0;
|
for (int col = 0; col < textureCols; ++col) {
|
||||||
float destX = -1.0f;
|
TextureInfo& ti = textureList[row * textureCols + col];
|
||||||
|
|
||||||
int sourceH = maxTextureSize;
|
// Width and height of the area read from the frame data
|
||||||
int textureH = maxTextureSize;
|
int sourceX = col * textureArea;
|
||||||
// If the last row doesn't need a full texture, shrink it to the smallest one possible
|
int sourceY = row * textureArea;
|
||||||
if (i == textureRows - 1 && height % maxTextureSize > 0) {
|
ti.sourceW = min(frameWidth - sourceX, maxTextureSize);
|
||||||
sourceH = height % maxTextureSize;
|
ti.sourceH = min(frameHeight - sourceY, maxTextureSize);
|
||||||
textureH = SmallestPowerOf2(sourceH);
|
|
||||||
|
// Used instead of GL_PACK_SKIP_ROWS/GL_PACK_SKIP_PIXELS due to
|
||||||
|
// performance issues with the emulation
|
||||||
|
ti.dataOffset = sourceY * frameWidth * bpp + sourceX * bpp;
|
||||||
|
|
||||||
|
int textureHeight = SmallestPowerOf2(ti.sourceH);
|
||||||
|
int textureWidth = SmallestPowerOf2(ti.sourceW);
|
||||||
|
if (!supportsRectangularTextures) {
|
||||||
|
textureWidth = textureHeight = max(textureWidth, textureHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Location where this texture is placed
|
||||||
|
// X2/Y2 will be offscreen unless the video frame happens to
|
||||||
|
// exactly use all of the texture
|
||||||
|
ti.destX1 = sourceX + (col != 0);
|
||||||
|
ti.destY1 = sourceY + (row != 0);
|
||||||
|
ti.destX2 = sourceX + textureWidth - (col != lastCol);
|
||||||
|
ti.destY2 = sourceY + textureHeight - (row != lastRow);
|
||||||
|
|
||||||
|
// Portion of the texture actually used
|
||||||
|
ti.texTop = row == 0 ? 0 : 1.0f / textureHeight;
|
||||||
|
ti.texLeft = col == 0 ? 0 : 1.0f / textureWidth;
|
||||||
|
ti.texBottom = row == lastRow ? 1.0f : 1.0f - 1.0f / textureHeight;
|
||||||
|
ti.texRight = col == lastCol ? 1.0f : 1.0f - 1.0f / textureWidth;
|
||||||
|
|
||||||
|
ti.textureID = textureIdList[row * textureCols + col];
|
||||||
|
|
||||||
|
CreateTexture(textureWidth, textureHeight, ti, format);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int j = 0; j < textureCols; j++) {
|
|
||||||
TextureInfo& ti = textureList[i * textureCols + j];
|
|
||||||
|
|
||||||
// Copy the current position information into the struct
|
|
||||||
ti.destX1 = destX;
|
|
||||||
ti.destY1 = destY;
|
|
||||||
ti.sourceH = sourceH;
|
|
||||||
ti.textureID = textureIdList[i * textureCols + j];
|
|
||||||
|
|
||||||
ti.sourceW = maxTextureSize;
|
|
||||||
int textureW = maxTextureSize;
|
|
||||||
// If the last column doesn't need a full texture, shrink it to the smallest one possible
|
|
||||||
if (j == textureCols - 1 && width % maxTextureSize > 0) {
|
|
||||||
ti.sourceW = width % maxTextureSize;
|
|
||||||
textureW = SmallestPowerOf2(ti.sourceW);
|
|
||||||
}
|
|
||||||
|
|
||||||
int w = textureW;
|
|
||||||
int h = textureH;
|
|
||||||
if (!supportsRectangularTextures) w = h = MAX(w, h);
|
|
||||||
|
|
||||||
CreateTexture(w, h, ti, format);
|
|
||||||
|
|
||||||
if (!supportsGlClampToEdge) {
|
|
||||||
// Stretch the texture a half pixel in each direction to eliminate the border
|
|
||||||
ti.texLeft = 1.0f / (2 * w);
|
|
||||||
ti.texTop = 1.0f / (2 * h);
|
|
||||||
}
|
|
||||||
|
|
||||||
ti.destX2 = ti.destX1 + w * 2.0f / width;
|
|
||||||
ti.destY2 = ti.destY1 + h * 2.0f / height;
|
|
||||||
|
|
||||||
ti.texRight = 1.0f - ti.texLeft;
|
|
||||||
if (flipped) {
|
|
||||||
ti.texBottom = 1.0f - ti.texTop;
|
|
||||||
ti.dataOffset = sourceY * width * bpp + sourceX * bpp;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
ti.texBottom = ti.texTop - float(h - ti.sourceH) / h;
|
|
||||||
ti.texTop = 1.0f - ti.texTop - float(h - ti.sourceH) / h;
|
|
||||||
|
|
||||||
ti.dataOffset = (height - sourceY - ti.sourceH) * width * bpp + sourceX * bpp;
|
|
||||||
}
|
|
||||||
|
|
||||||
destX = ti.destX2;
|
|
||||||
sourceX += ti.sourceW;
|
|
||||||
}
|
|
||||||
destY += sourceH * 2.0f / height;
|
|
||||||
sourceY += sourceH;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoOutGL::CreateTexture(int w, int h, const TextureInfo& ti, GLenum format) {
|
void VideoOutGL::CreateTexture(int w, int h, const TextureInfo& ti, GLenum format) {
|
||||||
CHECK_INIT_ERROR(glBindTexture(GL_TEXTURE_2D, ti.textureID));
|
CHECK_INIT_ERROR(glBindTexture(GL_TEXTURE_2D, ti.textureID));
|
||||||
CHECK_INIT_ERROR(glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, w, h, 0, format, GL_UNSIGNED_BYTE, NULL));
|
CHECK_INIT_ERROR(glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, w, h, 0, format, GL_UNSIGNED_BYTE, NULL));
|
||||||
wxLogDebug("VideoOutGL::InitTextures: Using texture size: %dx%d\n", w, h);
|
wxLogDebug(L"VideoOutGL::InitTextures: Using texture size: %dx%d\n", w, h);
|
||||||
CHECK_INIT_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
|
CHECK_INIT_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
|
||||||
CHECK_INIT_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
|
CHECK_INIT_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
|
||||||
|
CHECK_INIT_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP));
|
||||||
GLint mode = supportsGlClampToEdge ? GL_CLAMP_TO_EDGE : GL_CLAMP;
|
CHECK_INIT_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP));
|
||||||
CHECK_INIT_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, mode));
|
|
||||||
CHECK_INIT_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, mode));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoOutGL::UploadFrameData(const AegiVideoFrame& frame) {
|
void VideoOutGL::UploadFrameData(const AegiVideoFrame& frame) {
|
||||||
|
@ -300,6 +280,13 @@ void VideoOutGL::Render(int sw, int sh) {
|
||||||
|
|
||||||
CHECK_ERROR(glMatrixMode(GL_PROJECTION));
|
CHECK_ERROR(glMatrixMode(GL_PROJECTION));
|
||||||
CHECK_ERROR(glLoadIdentity());
|
CHECK_ERROR(glLoadIdentity());
|
||||||
|
CHECK_ERROR(glPushMatrix());
|
||||||
|
if (frameFlipped) {
|
||||||
|
CHECK_ERROR(glOrtho(0.0f, frameWidth, 0.0f, frameHeight, -1000.0f, 1000.0f));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
CHECK_ERROR(glOrtho(0.0f, frameWidth, frameHeight, 0.0f, -1000.0f, 1000.0f));
|
||||||
|
}
|
||||||
|
|
||||||
// Render the current frame
|
// Render the current frame
|
||||||
CHECK_ERROR(glEnable(GL_TEXTURE_2D));
|
CHECK_ERROR(glEnable(GL_TEXTURE_2D));
|
||||||
|
@ -320,6 +307,7 @@ void VideoOutGL::Render(int sw, int sh) {
|
||||||
}
|
}
|
||||||
CHECK_ERROR(glDisable(GL_TEXTURE_2D));
|
CHECK_ERROR(glDisable(GL_TEXTURE_2D));
|
||||||
|
|
||||||
|
CHECK_ERROR(glPopMatrix());
|
||||||
CHECK_ERROR(glOrtho(0.0f, sw, sh, 0.0f, -1000.0f, 1000.0f));
|
CHECK_ERROR(glOrtho(0.0f, sw, sh, 0.0f, -1000.0f, 1000.0f));
|
||||||
CHECK_ERROR(glMatrixMode(GL_MODELVIEW));
|
CHECK_ERROR(glMatrixMode(GL_MODELVIEW));
|
||||||
CHECK_ERROR(glLoadIdentity());
|
CHECK_ERROR(glLoadIdentity());
|
||||||
|
|
Loading…
Reference in a new issue