When OpenGL is available, prefer using the new glsurface to blit the classic renderer's 8-bit buffer to the screen.

git-svn-id: https://svn.eduke32.com/eduke32@6919 1a8010ca-5511-0410-912e-c29ae57300e0
This commit is contained in:
pogokeen 2018-06-13 19:15:16 +00:00
parent 38de24d7eb
commit 9d243606d0
6 changed files with 334 additions and 11 deletions

View file

@ -228,7 +228,7 @@ else
endif
endif
ifeq (1,$(USE_OPENGL))
engine_objs += voxmodel.cpp mdsprite.cpp tilepacker.cpp
engine_objs += glsurface.cpp voxmodel.cpp mdsprite.cpp tilepacker.cpp
engine_deps += glad
ifeq (1,$(POLYMER))
engine_objs += glbuild.cpp polymer.cpp

View file

@ -0,0 +1,41 @@
/*
* glsurface.cpp
* A 32-bit rendering surface that can quickly blit 8-bit paletted buffers implemented in OpenGL.
*
* Copyright © 2018, Alex Dawson. All rights reserved.
*/
#ifndef GLSURFACE_H_
#define GLSURFACE_H_
#include "compat.h"
#include "palette.h"
// Initialize the glsurface with the Software renderer's buffer resolution.
// If the Software renderer's resolution and the actual resolution don't match,
// glsurface will still render at the full size of the screen.
// If a surface already exists, glsurface_destroy() will be automatically called before re-initializing.
// Returns whether or not the glsurface could be successfully initialized.
bool glsurface_initialize(vec2_t inputBufferResolution);
// Destroy an existing surface.
void glsurface_destroy();
// Sets the palette at paletteID to contain the byte buffer pointed to by pPalette.
// If the surface is not initialized, the function returns immediately.
void glsurface_setPalette(int32_t paletteID, void* pPalette);
// Returns a pointer to the start of the surface's pixel buffer
// Returns NULL if the surface is not initialized.
void* glsurface_getBuffer();
// Returns the resolution of the surface's buffer
vec2_t glsurface_getBufferResolution();
// Blit the surface's pixel buffer to the screen.
// paletteID is the id of a palette previously set with glsurface_setPalette().
// Renders as soon as the data has been uploaded.
// If the surface is not initialized, the function returns immediately.
void glsurface_blitBuffer(int32_t paletteID);
#endif /* GLSURFACE_H_ */

View file

@ -23,6 +23,7 @@
#ifdef USE_OPENGL
# include "glad/glad.h"
# include "glsurface.h"
# include "mdsprite.h"
# ifdef POLYMER
# include "polymer.h"
@ -9898,6 +9899,11 @@ int32_t videoSetGameMode(char davidoption, int32_t daxdim, int32_t daydim, int32
#ifdef USE_OPENGL
fxdim = (float) daxdim;
fydim = (float) daydim;
if (videoGetRenderMode() == REND_CLASSIC && !nogl)
{
glsurface_initialize({xdim, ydim});
}
#endif
videoAllocateBuffers();

View file

@ -0,0 +1,238 @@
/*
* glsurface.cpp
* A 32-bit rendering surface that can quickly blit 8-bit paletted buffers implemented in OpenGL.
*
* Copyright © 2018, Alex Dawson. All rights reserved.
*/
#include "glsurface.h"
#include "glad/glad.h"
#include "baselayer.h"
#include "build.h"
static void* buffer;
static GLuint bufferTexID;
static vec2_t bufferRes;
static GLuint paletteTexIDs[MAXBASEPALS];
static GLuint quadVertsID = 0;
static GLuint shaderProgramID = 0;
static GLint texSamplerLoc = -1;
static GLint paletteSamplerLoc = -1;
static GLuint compileShader(GLenum shaderType, const char* const source)
{
GLuint shaderID = glCreateShader(shaderType);
if (shaderID == 0)
return 0;
const char* const sources[1] = {source};
glShaderSource(shaderID,
1,
sources,
NULL);
glCompileShader(shaderID);
GLint compileStatus;
glGetShaderiv(shaderID, GL_COMPILE_STATUS, &compileStatus);
OSD_Printf("Compile Status: %u\n", compileStatus);
if (!compileStatus)
{
GLint logLength;
glGetShaderiv(shaderID, GL_INFO_LOG_LENGTH, &logLength);
if (logLength > 0)
{
char *infoLog = (char*) malloc(logLength);
glGetShaderInfoLog(shaderID, logLength, &logLength, infoLog);
OSD_Printf("Log:\n%s\n", infoLog);
free(infoLog);
}
}
return shaderID;
}
bool glsurface_initialize(vec2_t inputBufferResolution)
{
if (buffer)
glsurface_destroy();
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
bufferRes = inputBufferResolution;
buffer = malloc(bufferRes.x*bufferRes.y);
glGenBuffers(1, &quadVertsID);
glBindBuffer(GL_ARRAY_BUFFER, quadVertsID);
const float quadVerts[] =
{
-1.0f, 1.0f, 0.0f, 0.0f, 0.0f, //top-left
-1.0f, -1.0f, 0.0f, 0.0f, 1.0f, //bottom-left
1.0f, 1.0f, 0.0f, 1.0f, 0.0f, //top-right
1.0f, -1.0f, 0.0f, 1.0f, 1.0f //bottom-right
};
glBufferData(GL_ARRAY_BUFFER, sizeof(quadVerts), quadVerts, GL_STATIC_DRAW);
//specify format/arrangement for vertex positions:
glVertexAttribPointer(0, 3, GL_FLOAT, false, sizeof(float) * 5, 0);
//specify format/arrangement for vertex texture coords:
glVertexAttribPointer(1, 2, GL_FLOAT, false, sizeof(float) * 5, (const void*) (sizeof(float) * 3));
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glGenTextures(1, &bufferTexID);
glBindTexture(GL_TEXTURE_2D, bufferTexID);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, bufferRes.x, bufferRes.y, 0, GL_RED, GL_UNSIGNED_BYTE, 0);
const char* const VERTEX_SHADER_CODE =
"#version 110\n\
\n\
attribute vec4 i_vertPos;\n\
attribute vec2 i_texCoord;\n\
\n\
varying vec2 v_texCoord;\n\
\n\
void main()\n\
{\n\
gl_Position = i_vertPos;\n\
v_texCoord = i_texCoord;\n\
}\n";
const char* const FRAGMENT_SHADER_CODE =
"#version 110\n\
\n\
//s_texture points to an indexed color texture\n\
uniform sampler2D s_texture;\n\
//s_palette is the palette texture\n\
uniform sampler2D s_palette;\n\
\n\
varying vec2 v_texCoord;\n\
\n\
const float c_paletteScale = 255.0/256.0;\n\
const float c_paletteOffset = 0.5/256.0;\n\
\n\
void main()\n\
{\n\
vec4 color = texture2D(s_texture, v_texCoord.xy);\n\
color.r = c_paletteOffset + c_paletteScale*color.r;\n\
color = texture2D(s_palette, color.rg);\n\
\n\
// DEBUG \n\
//color = texture2D(s_palette, v_texCoord.xy);\n\
//color = texture2D(s_texture, v_texCoord.xy);\n\
\n\
gl_FragColor = color;\n\
}\n";
shaderProgramID = glCreateProgram();
GLuint vertexShaderID = compileShader(GL_VERTEX_SHADER, VERTEX_SHADER_CODE);
GLuint fragmentShaderID = compileShader(GL_FRAGMENT_SHADER, FRAGMENT_SHADER_CODE);
glAttachShader(shaderProgramID, vertexShaderID);
glAttachShader(shaderProgramID, fragmentShaderID);
glBindAttribLocation(shaderProgramID, 0, "i_vertPos");
glBindAttribLocation(shaderProgramID, 1, "i_texCoord");
glLinkProgram(shaderProgramID);
glDetachShader(shaderProgramID, vertexShaderID);
glDeleteShader(vertexShaderID);
glDetachShader(shaderProgramID, fragmentShaderID);
glDeleteShader(fragmentShaderID);
glUseProgram(shaderProgramID);
texSamplerLoc = glGetUniformLocation(shaderProgramID, "s_texture");
paletteSamplerLoc = glGetUniformLocation(shaderProgramID, "s_palette");
glUniform1i(texSamplerLoc, 0);
glUniform1i(paletteSamplerLoc, 1);
for (int basepalnum = 0; basepalnum < MAXBASEPALS; ++basepalnum)
{
glsurface_setPalette(basepalnum, basepaltable[basepalnum]);
}
return true;
}
void glsurface_destroy()
{
if (!buffer)
return;
free(buffer);
buffer = 0;
glDeleteBuffers(1, &quadVertsID);
quadVertsID = 0;
glDeleteTextures(1, &bufferTexID);
bufferTexID = 0;
glDeleteTextures(MAXBASEPALS, paletteTexIDs);
memset(paletteTexIDs, 0, sizeof(paletteTexIDs));
glDeleteProgram(shaderProgramID);
shaderProgramID = 0;
}
void glsurface_setPalette(int32_t paletteID, void* pPalette)
{
if (!buffer)
return;
if (!pPalette)
return;
glActiveTexture(GL_TEXTURE1);
if (paletteTexIDs[paletteID])
{
glBindTexture(GL_TEXTURE_2D, paletteTexIDs[paletteID]);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 256, 1, GL_RGB, GL_UNSIGNED_BYTE, (void*) buffer);
}
else
{
glGenTextures(1, paletteTexIDs+paletteID);
glBindTexture(GL_TEXTURE_2D, paletteTexIDs[paletteID]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, 256, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, pPalette);
}
}
void* glsurface_getBuffer()
{
return buffer;
}
vec2_t glsurface_getBufferResolution()
{
return bufferRes;
}
void glsurface_blitBuffer(int32_t paletteID)
{
if (!buffer)
return;
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, paletteTexIDs[paletteID]);
glActiveTexture(GL_TEXTURE0);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, bufferRes.x, bufferRes.y, GL_RED, GL_UNSIGNED_BYTE, (void*) buffer);
glDrawArrays(GL_TRIANGLE_STRIP,
0,
4);
}

View file

@ -8,6 +8,10 @@
#include "a.h"
#include "xxhash.h"
#ifdef USE_OPENGL
# include "glsurface.h"
#endif
uint8_t *basepaltable[MAXBASEPALS] = { palette };
uint8_t basepalreset=1;
uint8_t curbasepal;
@ -656,7 +660,14 @@ void paletteSetColorTable(int32_t id, uint8_t const * const table)
Bmemcpy(basepaltable[id], table, 768);
#ifdef USE_OPENGL
uploadbasepalette(id);
if (videoGetRenderMode() == REND_CLASSIC)
{
glsurface_setPalette(id, basepaltable[id]);
}
else if (videoGetRenderMode() >= REND_POLYMOST)
{
uploadbasepalette(id);
}
#endif
}

View file

@ -16,6 +16,7 @@
#ifdef USE_OPENGL
# include "glad/glad.h"
# include "glbuild.h"
# include "glsurface.h"
#endif
#if defined _WIN32
@ -1300,6 +1301,7 @@ static void destroy_window_resources()
#ifdef USE_OPENGL
void sdlayer_setvideomode_opengl(void)
{
glsurface_destroy();
polymost_glreset();
glEnable(GL_TEXTURE_2D);
@ -1447,8 +1449,13 @@ int32_t setvideomode_sdlcommon(int32_t *x, int32_t *y, int32_t c, int32_t fs, in
while (lockcount) videoEndDrawing();
#ifdef USE_OPENGL
if (bpp > 8 && sdl_surface)
polymost_glreset();
if (sdl_surface)
{
if (bpp > 8)
polymost_glreset();
else if (!nogl)
glsurface_destroy();
}
#endif
// clear last gamma/contrast/brightness so that it will be set anew
@ -1462,7 +1469,7 @@ void setvideomode_sdlcommonpost(int32_t x, int32_t y, int32_t c, int32_t fs, int
wm_setapptitle(apptitle);
#ifdef USE_OPENGL
if (c > 8)
if (!nogl)
sdlayer_setvideomode_opengl();
#endif
@ -1587,7 +1594,7 @@ int32_t videoSetMode(int32_t x, int32_t y, int32_t c, int32_t fs)
initprintf("Setting video mode %dx%d (%d-bpp %s)\n", x, y, c, ((fs & 1) ? "fullscreen" : "windowed"));
#ifdef USE_OPENGL
if (c > 8)
if (c > 8 || !nogl)
{
int32_t i, j;
#ifdef USE_GLEXT
@ -1722,6 +1729,19 @@ void videoBeginDrawing(void)
modechange = 0;
return;
}
else if (!nogl)
{
if (offscreenrendering) return;
frameplace = (intptr_t)glsurface_getBuffer();
if (modechange)
{
bytesperline = xdim;
calc_ylookup(bytesperline, ydim);
modechange=0;
}
return;
}
// lock the frame
if (lockcount++ > 0)
@ -1748,7 +1768,7 @@ void videoBeginDrawing(void)
//
void videoEndDrawing(void)
{
if (bpp > 8)
if (bpp > 8 || !nogl)
{
if (!offscreenrendering) frameplace = 0;
return;
@ -1783,14 +1803,21 @@ void videoShowFrame(int32_t w)
#endif
#ifdef USE_OPENGL
if (bpp > 8)
if (!nogl)
{
if (palfadedelta)
fullscreen_tint_gl(palfadergb.r, palfadergb.g, palfadergb.b, palfadedelta);
if (bpp > 8)
{
if (palfadedelta)
fullscreen_tint_gl(palfadergb.r, palfadergb.g, palfadergb.b, palfadedelta);
#ifdef __ANDROID__
AndroidDrawControls();
AndroidDrawControls();
#endif
}
else
{
glsurface_blitBuffer(curbasepal);
}
static uint32_t lastSwapTime = 0;
SDL_GL_SwapWindow(sdl_window);