raze/source/common/rendering/gl/gl_hwtexture.cpp
Christoph Oelckers 594ec6626c - transitioned to using GZDoom's texture creation code.
Also added support for creating indexed textures directly into CreateTexBuffer, where this functionality can be shared.
As an added plus, brightmaps are working again, this time with less hackery.
2020-05-29 19:21:18 +02:00

339 lines
9.7 KiB
C++

/*
** gl_hwtexture.cpp
** GL texture abstraction
**
**---------------------------------------------------------------------------
** Copyright 2019 Christoph Oelckers
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
**
*/
#include "gl_system.h"
#include "templates.h"
#include "c_cvars.h"
#include "hw_material.h"
#include "gl_interface.h"
#include "hw_cvars.h"
#include "gl_debug.h"
#include "gl_renderer.h"
#include "gl_samplers.h"
#include "gl_hwtexture.h"
namespace OpenGLRenderer
{
TexFilter_s TexFilter[] = {
{GL_NEAREST, GL_NEAREST, false},
{GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST, true},
{GL_LINEAR, GL_LINEAR, false},
{GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR, true},
{GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR, true},
{GL_NEAREST_MIPMAP_LINEAR, GL_NEAREST, true},
{GL_LINEAR_MIPMAP_LINEAR, GL_NEAREST, true},
};
//===========================================================================
//
// Static texture data
//
//===========================================================================
unsigned int FHardwareTexture::lastbound[FHardwareTexture::MAX_TEXTURES];
//===========================================================================
//
// Loads the texture image into the hardware
//
// NOTE: For some strange reason I was unable to find the source buffer
// should be one line higher than the actual texture. I got extremely
// strange crashes deep inside the GL driver when I didn't do it!
//
//===========================================================================
unsigned int FHardwareTexture::CreateTexture(unsigned char * buffer, int w, int h, int texunit, bool mipmap, const char *name)
{
int rh,rw;
int texformat = GL_RGBA8;// TexFormat[gl_texture_format];
bool deletebuffer=false;
/*
if (forcenocompression)
{
texformat = GL_RGBA8;
}
*/
bool firstCall = glTexID == 0;
if (firstCall)
{
glGenTextures(1, &glTexID);
}
int textureBinding = UINT_MAX;
if (texunit == -1) glGetIntegerv(GL_TEXTURE_BINDING_2D, &textureBinding);
if (texunit > 0) glActiveTexture(GL_TEXTURE0+texunit);
if (texunit >= 0) lastbound[texunit] = glTexID;
glBindTexture(GL_TEXTURE_2D, glTexID);
FGLDebug::LabelObject(GL_TEXTURE, glTexID, name);
rw = GetTexDimension(w);
rh = GetTexDimension(h);
if (glBufferID > 0)
{
glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
buffer = nullptr;
}
else if (!buffer)
{
// The texture must at least be initialized if no data is present.
mipmapped = false;
buffer=(unsigned char *)calloc(4,rw * (rh+1));
deletebuffer=true;
//texheight=-h;
}
else
{
if (rw < w || rh < h)
{
// The texture is larger than what the hardware can handle so scale it down.
unsigned char * scaledbuffer=(unsigned char *)calloc(4,rw * (rh+1));
if (scaledbuffer)
{
Resize(w, h, rw, rh, buffer, scaledbuffer);
deletebuffer=true;
buffer=scaledbuffer;
}
}
}
// store the physical size.
int sourcetype;
if (glTextureBytes > 0)
{
if (glTextureBytes < 4) glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
static const int ITypes[] = { GL_R8, GL_RG8, GL_RGB8, GL_RGBA8 };
static const int STypes[] = { GL_RED, GL_RG, GL_BGR, GL_BGRA };
texformat = ITypes[glTextureBytes - 1];
sourcetype = STypes[glTextureBytes - 1];
}
else
{
sourcetype = GL_BGRA;
}
if (!firstCall && glBufferID > 0)
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, rw, rh, sourcetype, GL_UNSIGNED_BYTE, buffer);
else
glTexImage2D(GL_TEXTURE_2D, 0, texformat, rw, rh, 0, sourcetype, GL_UNSIGNED_BYTE, buffer);
if (deletebuffer && buffer) free(buffer);
else if (glBufferID)
{
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
}
if (mipmap && TexFilter[gl_texture_filter].mipmapping)
{
glGenerateMipmap(GL_TEXTURE_2D);
mipmapped = true;
}
if (texunit > 0) glActiveTexture(GL_TEXTURE0);
else if (texunit == -1) glBindTexture(GL_TEXTURE_2D, textureBinding);
return glTexID;
}
//===========================================================================
//
//
//
//===========================================================================
void FHardwareTexture::AllocateBuffer(int w, int h, int texelsize)
{
int rw = GetTexDimension(w);
int rh = GetTexDimension(h);
if (texelsize < 1 || texelsize > 4) texelsize = 4;
glTextureBytes = texelsize;
bufferpitch = w;
if (rw == w || rh == h)
{
glGenBuffers(1, &glBufferID);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, glBufferID);
glBufferData(GL_PIXEL_UNPACK_BUFFER, w*h*texelsize, nullptr, GL_STREAM_DRAW);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
}
}
uint8_t *FHardwareTexture::MapBuffer()
{
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, glBufferID);
return (uint8_t*)glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY);
}
//===========================================================================
//
// Destroys the texture
//
//===========================================================================
FHardwareTexture::~FHardwareTexture()
{
if (glTexID != 0) glDeleteTextures(1, &glTexID);
if (glBufferID != 0) glDeleteBuffers(1, &glBufferID);
}
//===========================================================================
//
// Binds this patch
//
//===========================================================================
unsigned int FHardwareTexture::Bind(int texunit, bool needmipmap)
{
if (glTexID != 0)
{
if (lastbound[texunit] == glTexID) return glTexID;
lastbound[texunit] = glTexID;
if (texunit != 0) glActiveTexture(GL_TEXTURE0 + texunit);
glBindTexture(GL_TEXTURE_2D, glTexID);
// Check if we need mipmaps on a texture that was creted without them.
if (needmipmap && !mipmapped && TexFilter[gl_texture_filter].mipmapping)
{
glGenerateMipmap(GL_TEXTURE_2D);
mipmapped = true;
}
if (texunit != 0) glActiveTexture(GL_TEXTURE0);
return glTexID;
}
return 0;
}
void FHardwareTexture::Unbind(int texunit)
{
if (lastbound[texunit] != 0)
{
if (texunit != 0) glActiveTexture(GL_TEXTURE0+texunit);
glBindTexture(GL_TEXTURE_2D, 0);
if (texunit != 0) glActiveTexture(GL_TEXTURE0);
lastbound[texunit] = 0;
}
}
void FHardwareTexture::UnbindAll()
{
for(int texunit = 0; texunit < 16; texunit++)
{
Unbind(texunit);
}
}
//===========================================================================
//
// Creates a depth buffer for this texture
//
//===========================================================================
int FHardwareTexture::GetDepthBuffer(int width, int height)
{
if (glDepthID == 0)
{
glGenRenderbuffers(1, &glDepthID);
glBindRenderbuffer(GL_RENDERBUFFER, glDepthID);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8,
GetTexDimension(width), GetTexDimension(height));
glBindRenderbuffer(GL_RENDERBUFFER, 0);
}
return glDepthID;
}
//===========================================================================
//
// Binds this texture's surfaces to the current framrbuffer
//
//===========================================================================
void FHardwareTexture::BindToFrameBuffer(int width, int height)
{
width = GetTexDimension(width);
height = GetTexDimension(height);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, glTexID, 0);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, GetDepthBuffer(width, height));
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, GetDepthBuffer(width, height));
}
//===========================================================================
//
// Binds a texture to the renderer
//
//===========================================================================
bool FHardwareTexture::BindOrCreate(FTexture *tex, int texunit, int clampmode, int translation, int flags)
{
int usebright = false;
bool needmipmap = (clampmode <= CLAMP_XY) && !forcenofilter;
// Bind it to the system.
if (!Bind(texunit, needmipmap))
{
int w = 0, h = 0;
// Create this texture
FTextureBuffer texbuffer;
if (!tex->isHardwareCanvas())
{
texbuffer = tex->CreateTexBuffer(translation, flags | CTF_ProcessData);
w = texbuffer.mWidth;
h = texbuffer.mHeight;
}
else
{
w = tex->GetWidth();
h = tex->GetHeight();
}
if (!CreateTexture(texbuffer.mBuffer, w, h, texunit, needmipmap, "FHardwareTexture.BindOrCreate"))
{
// could not create texture
return false;
}
}
if (forcenofilter) clampmode += CLAMP_NOFILTER - CLAMP_NONE;
GLRenderer->mSamplerManager->Bind(texunit, clampmode, 255);
return true;
}
}