raze/source/common/rendering/gl/gl_hwtexture.cpp
2021-10-30 10:51:03 +02:00

345 lines
9.8 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 "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_renderstate.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))
{
if (flags & CTF_Indexed)
{
glTextureBytes = 1;
forcenofilter = true;
needmipmap = false;
}
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_XY) clampmode += CLAMP_NOFILTER - CLAMP_NONE;
GLRenderer->mSamplerManager->Bind(texunit, clampmode, 255);
return true;
}
}