scummvm/graphics/tinygl/zbuffer.cpp
Vincent Pelletier f8c72b5967 TINYGL: Move texture resampling from create time to render time
Thanks to pixel repacking using cpu data cache, this has a low runtime
cost even for TGL_LINEAR.
Adds support for TGL_NEAREST.
Adds support for TGL_MIRRORED_REPEAT, TGL_CLAMP_TO_EDGE and TGL_REPEAT.
Also, add support for more texture clamping and resampling options.

Mipmaps are not supported, although their sampling modes will be
applied - but to the full-size texture instead of selected mipmap.

Note: Texture sampler is still chosen at texture creation time.
2020-11-12 08:15:39 +01:00

334 lines
11 KiB
C++

/* ResidualVM - A 3D game interpreter
*
* ResidualVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the AUTHORS
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
/*
* This file is based on, or a modified version of code from TinyGL (C) 1997-1998 Fabrice Bellard,
* which is licensed under the zlib-license (see LICENSE).
* It also has modifications by the ResidualVM-team, which are covered under the GPLv2 (or later).
*/
// Z buffer: 16,32 bits Z / 16 bits color
#include "common/scummsys.h"
#include "common/endian.h"
#include "graphics/tinygl/zbuffer.h"
#include "graphics/tinygl/zgl.h"
namespace TinyGL {
// adr must be aligned on an 'int'
void memset_s(void *adr, int val, int count) {
int n, v;
unsigned int *p;
unsigned short *q;
p = (unsigned int *)adr;
v = val | (val << 16);
n = count >> 3;
for (int i = 0; i < n; i++) {
p[0] = v;
p[1] = v;
p[2] = v;
p[3] = v;
p += 4;
}
q = (unsigned short *) p;
n = count & 7;
for (int i = 0; i < n; i++)
*q++ = val;
}
void memset_l(void *adr, int val, int count) {
int n, v;
unsigned int *p;
p = (unsigned int *)adr;
v = val;
n = count >> 2;
for (int i = 0; i < n; i++) {
p[0] = v;
p[1] = v;
p[2] = v;
p[3] = v;
p += 4;
}
n = count & 3;
for (int i = 0; i < n; i++)
*p++ = val;
}
FrameBuffer::FrameBuffer(int width, int height, const Graphics::PixelBuffer &frame_buffer) : _depthWrite(true), _enableScissor(false) {
this->xsize = width;
this->ysize = height;
this->cmode = frame_buffer.getFormat();
this->pixelbytes = this->cmode.bytesPerPixel;
this->linesize = (xsize * this->pixelbytes + 3) & ~3;
int size = this->xsize * this->ysize * sizeof(unsigned int);
this->_zbuf = (unsigned int *)gl_malloc(size);
memset(this->_zbuf, 0, size);
this->frame_buffer_allocated = 0;
this->pbuf = frame_buffer;
this->current_texture = NULL;
this->shadow_mask_buf = NULL;
this->buffer.pbuf = this->pbuf.getRawBuffer();
this->buffer.zbuf = this->_zbuf;
_blendingEnabled = false;
_alphaTestEnabled = false;
_depthTestEnabled = false;
_depthFunc = TGL_LESS;
}
FrameBuffer::FrameBuffer(int width, int height, const Graphics::PixelFormat &format) : _depthWrite(true), _enableScissor(false) {
this->xsize = width;
this->ysize = height;
this->cmode = format;
this->pixelbytes = this->cmode.bytesPerPixel;
this->linesize = (xsize * this->pixelbytes + 3) & ~3;
int size = this->xsize * this->ysize * sizeof(unsigned int);
this->_zbuf = (unsigned int *)gl_malloc(size);
memset(this->_zbuf, 0, size);
byte *pixelBuffer = (byte *)gl_malloc(this->ysize * this->linesize);
this->pbuf.set(this->cmode, pixelBuffer);
this->frame_buffer_allocated = 1;
this->current_texture = NULL;
this->shadow_mask_buf = NULL;
this->buffer.pbuf = this->pbuf.getRawBuffer();
this->buffer.zbuf = this->_zbuf;
_blendingEnabled = false;
_alphaTestEnabled = false;
_depthTestEnabled = false;
_depthFunc = TGL_LESS;
}
FrameBuffer::~FrameBuffer() {
if (frame_buffer_allocated)
pbuf.free();
gl_free(_zbuf);
}
Buffer *FrameBuffer::genOffscreenBuffer() {
Buffer *buf = (Buffer *)gl_malloc(sizeof(Buffer));
buf->pbuf = (byte *)gl_malloc(this->ysize * this->linesize);
int size = this->xsize * this->ysize * sizeof(unsigned int);
buf->zbuf = (unsigned int *)gl_malloc(size);
return buf;
}
void FrameBuffer::delOffscreenBuffer(Buffer *buf) {
gl_free(buf->pbuf);
gl_free(buf->zbuf);
gl_free(buf);
}
void FrameBuffer::clear(int clearZ, int z, int clearColor, int r, int g, int b) {
if (clearZ) {
const uint8 *zc = (const uint8 *)&z;
unsigned int i;
for (i = 1; i < sizeof(z) && zc[0] == zc[i]; i++) { ; }
if (i == sizeof(z)) {
// All "z" bytes are identical, use memset (fast)
memset(this->_zbuf, zc[0], sizeof(*this->_zbuf) * this->xsize * this->ysize);
} else {
// Cannot use memset, use a variant working on integers (slow)
memset_l(this->_zbuf, z, this->xsize * this->ysize);
}
}
if (clearColor) {
byte *pp = this->pbuf.getRawBuffer();
uint32 color = this->cmode.RGBToColor(r, g, b);
const uint8 *colorc = (uint8 *)&color;
unsigned int i;
for (i = 1; i < sizeof(color) && colorc[0] == colorc[i]; i++) { ; }
if (i == sizeof(color)) {
// All "color" bytes are identical, use memset (fast)
memset(pp, colorc[0], this->linesize * this->ysize);
} else {
// Cannot use memset, use a variant working on shorts/ints (slow)
switch(this->pixelbytes) {
case 2:
memset_s(pp, color, this->xsize * this->ysize);
break;
case 4:
memset_l(pp, color, this->xsize * this->ysize);
break;
default:
error("Unsupported pixel size %i", this->pixelbytes);
}
}
}
}
void FrameBuffer::clearRegion(int x, int y, int w, int h, int clearZ, int z, int clearColor, int r, int g, int b) {
if (clearZ) {
int height = h;
unsigned int *zbuf = this->_zbuf + (y * this->xsize);
const uint8 *zc = (const uint8 *)&z;
unsigned int i;
for (i = 1; i < sizeof(z) && zc[0] == zc[i]; i++) { ; }
if (i == sizeof(z)) {
// All "z" bytes are identical, use memset (fast)
while (height--) {
memset(zbuf + x, zc[0], sizeof(*zbuf) * w);
zbuf += this->xsize;
}
} else {
// Cannot use memset, use a variant working on integers (slow)
while (height--) {
memset_l(zbuf + x, z, w);
zbuf += this->xsize;
}
}
}
if (clearColor) {
int height = h;
byte *pp = this->pbuf.getRawBuffer() + y * this->linesize + x * this->pixelbytes;
uint32 color = this->cmode.RGBToColor(r, g, b);
const uint8 *colorc = (uint8 *)&color;
unsigned int i;
for (i = 1; i < sizeof(color) && colorc[0] == colorc[i]; i++) { ; }
if (i == sizeof(color)) {
// All "color" bytes are identical, use memset (fast)
while (height--) {
memset(pp, colorc[0], this->pixelbytes * w);
pp += this->linesize;
}
} else {
// Cannot use memset, use a variant working on shorts/ints (slow)
while (height--) {
switch(this->pixelbytes) {
case 2:
memset_s(pp, color, w);
break;
case 4:
memset_l(pp, color, w);
break;
default:
error("Unsupported pixel size %i", this->pixelbytes);
}
pp += this->linesize;
}
}
}
}
inline static void blitPixel(uint8 offset, unsigned int *from_z, unsigned int *to_z, unsigned int z_length, byte *from_color, byte *to_color, unsigned int color_length) {
const unsigned int d = from_z[offset];
if (d > to_z[offset]) {
memcpy(to_color + offset, from_color + offset, color_length);
memcpy(to_z + offset, &d, z_length);
}
}
void FrameBuffer::blitOffscreenBuffer(Buffer *buf) {
// TODO: could be faster, probably.
#define UNROLL_COUNT 16
if (buf->used) {
const int pixel_bytes = this->pixelbytes;
const int unrolled_pixel_bytes = pixel_bytes * UNROLL_COUNT;
byte *to = this->pbuf.getRawBuffer();
byte *from = buf->pbuf;
unsigned int *to_z = this->_zbuf;
unsigned int *from_z = buf->zbuf;
int count = this->xsize * this->ysize;
while (count >= UNROLL_COUNT) {
blitPixel(0x0, from_z, to_z, sizeof(int), from, to, pixel_bytes);
blitPixel(0x1, from_z, to_z, sizeof(int), from, to, pixel_bytes);
blitPixel(0x2, from_z, to_z, sizeof(int), from, to, pixel_bytes);
blitPixel(0x3, from_z, to_z, sizeof(int), from, to, pixel_bytes);
blitPixel(0x4, from_z, to_z, sizeof(int), from, to, pixel_bytes);
blitPixel(0x5, from_z, to_z, sizeof(int), from, to, pixel_bytes);
blitPixel(0x6, from_z, to_z, sizeof(int), from, to, pixel_bytes);
blitPixel(0x7, from_z, to_z, sizeof(int), from, to, pixel_bytes);
blitPixel(0x8, from_z, to_z, sizeof(int), from, to, pixel_bytes);
blitPixel(0x9, from_z, to_z, sizeof(int), from, to, pixel_bytes);
blitPixel(0xA, from_z, to_z, sizeof(int), from, to, pixel_bytes);
blitPixel(0xB, from_z, to_z, sizeof(int), from, to, pixel_bytes);
blitPixel(0xC, from_z, to_z, sizeof(int), from, to, pixel_bytes);
blitPixel(0xD, from_z, to_z, sizeof(int), from, to, pixel_bytes);
blitPixel(0xE, from_z, to_z, sizeof(int), from, to, pixel_bytes);
blitPixel(0xF, from_z, to_z, sizeof(int), from, to, pixel_bytes);
count -= UNROLL_COUNT;
to += unrolled_pixel_bytes;
from += unrolled_pixel_bytes;
to_z += UNROLL_COUNT;
}
switch (count) {
case 0xF: blitPixel(0xE, from_z, to_z, sizeof(int), from, to, pixel_bytes); // fall through
case 0xE: blitPixel(0xD, from_z, to_z, sizeof(int), from, to, pixel_bytes); // fall through
case 0xD: blitPixel(0xC, from_z, to_z, sizeof(int), from, to, pixel_bytes); // fall through
case 0xC: blitPixel(0xB, from_z, to_z, sizeof(int), from, to, pixel_bytes); // fall through
case 0xB: blitPixel(0xA, from_z, to_z, sizeof(int), from, to, pixel_bytes); // fall through
case 0xA: blitPixel(0x9, from_z, to_z, sizeof(int), from, to, pixel_bytes); // fall through
case 0x9: blitPixel(0x8, from_z, to_z, sizeof(int), from, to, pixel_bytes); // fall through
case 0x8: blitPixel(0x7, from_z, to_z, sizeof(int), from, to, pixel_bytes); // fall through
case 0x7: blitPixel(0x6, from_z, to_z, sizeof(int), from, to, pixel_bytes); // fall through
case 0x6: blitPixel(0x5, from_z, to_z, sizeof(int), from, to, pixel_bytes); // fall through
case 0x5: blitPixel(0x4, from_z, to_z, sizeof(int), from, to, pixel_bytes); // fall through
case 0x4: blitPixel(0x3, from_z, to_z, sizeof(int), from, to, pixel_bytes); // fall through
case 0x3: blitPixel(0x2, from_z, to_z, sizeof(int), from, to, pixel_bytes); // fall through
case 0x2: blitPixel(0x1, from_z, to_z, sizeof(int), from, to, pixel_bytes); // fall through
case 0x1: blitPixel(0x0, from_z, to_z, sizeof(int), from, to, pixel_bytes); // fall through
case 0x0: break;
}
}
#undef UNROLL_COUNT
}
void FrameBuffer::selectOffscreenBuffer(Buffer *buf) {
if (buf) {
this->pbuf = buf->pbuf;
this->_zbuf = buf->zbuf;
buf->used = true;
} else {
this->pbuf = this->buffer.pbuf;
this->_zbuf = this->buffer.zbuf;
}
}
void FrameBuffer::clearOffscreenBuffer(Buffer *buf) {
memset(buf->pbuf, 0, this->ysize * this->linesize);
memset(buf->zbuf, 0, this->ysize * this->xsize * sizeof(unsigned int));
buf->used = false;
}
void FrameBuffer::setTexture(const Graphics::TexelBuffer *texture, unsigned int wraps, unsigned int wrapt) {
current_texture = texture;
wrapS = wraps;
wrapT = wrapt;
}
} // end of namespace TinyGL