mirror of
https://github.com/copyrighttxt/watrbx-game-engine.git
synced 2026-09-04 20:57:49 +00:00
550 lines
18 KiB
C++
550 lines
18 KiB
C++
#include "TextureGL.h"
|
|
|
|
#include "DeviceGL.h"
|
|
#include "FramebufferGL.h"
|
|
|
|
#include "HeadersGL.h"
|
|
|
|
LOGGROUP(Graphics)
|
|
|
|
FASTFLAG(GraphicsTextureCommitChanges)
|
|
|
|
namespace RBX
|
|
{
|
|
namespace Graphics
|
|
{
|
|
|
|
static const GLenum gTextureTargetGL[Texture::Type_Count] =
|
|
{
|
|
GL_TEXTURE_2D,
|
|
GL_TEXTURE_3D,
|
|
GL_TEXTURE_CUBE_MAP
|
|
};
|
|
|
|
struct TextureFormatGL
|
|
{
|
|
GLenum internalFormat;
|
|
GLenum dataFormat;
|
|
GLenum dataType;
|
|
};
|
|
|
|
static const TextureFormatGL gTextureFormatGL[Texture::Format_Count] =
|
|
{
|
|
{ GL_R8, GL_RED, GL_UNSIGNED_BYTE },
|
|
{ GL_RG8, GL_RG, GL_UNSIGNED_BYTE },
|
|
{ GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1 },
|
|
{ GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE },
|
|
{ GL_RG16, GL_RG, GL_UNSIGNED_SHORT },
|
|
{ GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT },
|
|
{ GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, 0, 0 },
|
|
{ GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, 0, 0 },
|
|
{ GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, 0, 0 },
|
|
{ GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG, 0, 0 },
|
|
{ GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG, 0, 0 },
|
|
{ GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG, 0, 0 },
|
|
{ GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG, 0, 0 },
|
|
{ GL_ETC1_RGB8_OES, 0, 0 },
|
|
{ GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT },
|
|
{ GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8 },
|
|
};
|
|
|
|
const TextureFormatGL& getTextureFormatGL(Texture::Format format, bool ext3)
|
|
{
|
|
if (!ext3 && format == Texture::Format_L8)
|
|
{
|
|
static const TextureFormatGL result = { GL_LUMINANCE8, GL_LUMINANCE, GL_UNSIGNED_BYTE };
|
|
return result;
|
|
}
|
|
|
|
if (!ext3 && format == Texture::Format_LA8)
|
|
{
|
|
static const TextureFormatGL result = { GL_LUMINANCE8_ALPHA8, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE };
|
|
return result;
|
|
}
|
|
|
|
return gTextureFormatGL[format];
|
|
}
|
|
|
|
static const GLenum gSamplerFilterMinGL[SamplerState::Filter_Count][2] =
|
|
{
|
|
{ GL_NEAREST, GL_NEAREST_MIPMAP_NEAREST },
|
|
{ GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR },
|
|
{ GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR }
|
|
};
|
|
|
|
static const GLenum gSamplerFilterMagGL[SamplerState::Filter_Count] =
|
|
{
|
|
GL_NEAREST,
|
|
GL_LINEAR,
|
|
GL_LINEAR
|
|
};
|
|
|
|
static const GLenum gSamplerAddressGL[SamplerState::Address_Count] =
|
|
{
|
|
GL_REPEAT,
|
|
GL_CLAMP_TO_EDGE
|
|
};
|
|
|
|
static bool doesFormatSupportPartialUpload(Texture::Format format)
|
|
{
|
|
return format != Texture::Format_ETC1;
|
|
}
|
|
|
|
static unsigned int createTexture(Texture::Type type, Texture::Format format, unsigned int width, unsigned int height, unsigned int depth, unsigned int mipLevels, const DeviceCapsGL& caps)
|
|
{
|
|
GLenum target = gTextureTargetGL[type];
|
|
const TextureFormatGL& formatGl = getTextureFormatGL(format, caps.ext3);
|
|
|
|
unsigned int id = 0;
|
|
|
|
glActiveTexture(GL_TEXTURE0);
|
|
|
|
glGenTextures(1, &id);
|
|
glBindTexture(target, id);
|
|
|
|
if (caps.extTextureStorage)
|
|
{
|
|
if (type == Texture::Type_3D)
|
|
{
|
|
glTexStorage3D(target, mipLevels, formatGl.internalFormat, width, height, depth);
|
|
}
|
|
else
|
|
{
|
|
glTexStorage2D(target, mipLevels, formatGl.internalFormat, width, height);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Is this correct? There's a subtle difference here between desktop GL and GLES
|
|
#ifdef GLES
|
|
GLenum internalFormat = formatGl.dataFormat;
|
|
#else
|
|
GLenum internalFormat = formatGl.internalFormat;
|
|
#endif
|
|
|
|
unsigned int faces = (type == Texture::Type_Cube) ? 6 : 1;
|
|
|
|
for (unsigned int index = 0; index < faces; ++index)
|
|
{
|
|
GLenum faceTarget = (type == Texture::Type_Cube) ? GL_TEXTURE_CUBE_MAP_POSITIVE_X + index : target;
|
|
|
|
for (unsigned int mip = 0; mip < mipLevels; ++mip)
|
|
{
|
|
unsigned int mipWidth = Texture::getMipSide(width, mip);
|
|
unsigned int mipHeight = Texture::getMipSide(height, mip);
|
|
unsigned int mipDepth = Texture::getMipSide(depth, mip);
|
|
|
|
if (type == Texture::Type_3D)
|
|
{
|
|
glTexImage3D(faceTarget, mip, internalFormat, mipWidth, mipHeight, mipDepth, 0, formatGl.dataFormat, formatGl.dataType, NULL);
|
|
}
|
|
else if (Texture::isFormatCompressed(format))
|
|
{
|
|
unsigned int size = Texture::getImageSize(format, mipWidth, mipHeight);
|
|
|
|
// glCompressedTexImage does not accept NULL as a valid pointer
|
|
std::vector<char> data(size);
|
|
|
|
glCompressedTexImage2D(faceTarget, mip, formatGl.internalFormat, mipWidth, mipHeight, 0, size, &data[0]);
|
|
}
|
|
else
|
|
{
|
|
glTexImage2D(faceTarget, mip, internalFormat, mipWidth, mipHeight, 0, formatGl.dataFormat, formatGl.dataType, NULL);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (caps.supportsTexturePartialMipChain)
|
|
glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, mipLevels - 1);
|
|
else
|
|
RBXASSERT(mipLevels == 1 || mipLevels == Texture::getMaxMipCount(width, height, depth));
|
|
|
|
glBindTexture(target, 0);
|
|
|
|
return id;
|
|
}
|
|
|
|
static void uploadTexture(unsigned int id, Texture::Type type, Texture::Format format, unsigned int index, unsigned int mip, const TextureRegion& region, bool entireRegion, const void* data, const DeviceCapsGL& caps)
|
|
{
|
|
GLenum target = gTextureTargetGL[type];
|
|
GLenum faceTarget = (type == Texture::Type_Cube) ? GL_TEXTURE_CUBE_MAP_POSITIVE_X + index : target;
|
|
const TextureFormatGL& formatGl = getTextureFormatGL(format, caps.ext3);
|
|
|
|
glActiveTexture(GL_TEXTURE0);
|
|
glBindTexture(target, id);
|
|
|
|
bool needsCustomAlignment = (!Texture::isFormatCompressed(format) && Texture::getFormatBits(format) < 32);
|
|
|
|
glPixelStorei(GL_UNPACK_ALIGNMENT, needsCustomAlignment ? 1 : 4);
|
|
|
|
if (type == Texture::Type_3D)
|
|
{
|
|
glTexSubImage3D(faceTarget, mip, region.x, region.y, region.z, region.width, region.height, region.depth, formatGl.dataFormat, formatGl.dataType, data);
|
|
}
|
|
else
|
|
{
|
|
if (Texture::isFormatCompressed(format))
|
|
{
|
|
unsigned int size = Texture::getImageSize(format, region.width, region.height) * region.depth;
|
|
|
|
if (entireRegion && !doesFormatSupportPartialUpload(format) && !caps.extTextureStorage)
|
|
{
|
|
glCompressedTexImage2D(faceTarget, mip, formatGl.internalFormat, region.width, region.height, 0, size, data);
|
|
}
|
|
else
|
|
{
|
|
glCompressedTexSubImage2D(faceTarget, mip, region.x, region.y, region.width, region.height, formatGl.internalFormat, size, data);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
glTexSubImage2D(faceTarget, mip, region.x, region.y, region.width, region.height, formatGl.dataFormat, formatGl.dataType, data);
|
|
}
|
|
}
|
|
|
|
glBindTexture(target, 0);
|
|
}
|
|
|
|
TextureGL::TextureGL(Device* device, Type type, Format format, unsigned int width, unsigned int height, unsigned int depth, unsigned int mipLevels, Usage usage)
|
|
: Texture(device, type, format, width, height, depth, mipLevels, usage)
|
|
, id(0)
|
|
, cachedState(SamplerState::Filter_Count) // initialize state to invalid value to force a cache miss on the next setup
|
|
, external(false)
|
|
, scratchId(0)
|
|
, scratchOffset(0)
|
|
, scratchSize(0)
|
|
{
|
|
const DeviceCapsGL& caps = static_cast<DeviceGL*>(device)->getCapsGL();
|
|
|
|
const TextureFormatGL& formatGl = getTextureFormatGL(format, caps.ext3);
|
|
|
|
if (!formatGl.internalFormat)
|
|
throw RBX::runtime_error("Unsupported format: %d (platform)", format);
|
|
|
|
if ((format == Format_RGBA16F) && !caps.supportsTextureHalfFloat)
|
|
throw RBX::runtime_error("Unsupported format: %d (caps)", format);
|
|
|
|
if ((format == Format_BC1 || format == Format_BC2 || format == Format_BC3) && !caps.supportsTextureDXT)
|
|
throw RBX::runtime_error("Unsupported format: %d (caps)", format);
|
|
|
|
if ((format == Format_PVRTC_RGB2 || format == Format_PVRTC_RGBA2 || format == Format_PVRTC_RGB4 || format == Format_PVRTC_RGBA4) && !caps.supportsTexturePVR)
|
|
throw RBX::runtime_error("Unsupported format: %d (caps)", format);
|
|
|
|
if (format == Format_ETC1 && !caps.supportsTextureETC1)
|
|
throw RBX::runtime_error("Unsupported format: %d (capse)", format);
|
|
|
|
if (!caps.supportsTextureNPOT && ((width & (width - 1)) || (height & (height - 1)) || (depth & (depth - 1))) && (usage != Usage_Renderbuffer || mipLevels > 1))
|
|
throw RBX::runtime_error("Unsupported dimensions: %dx%dx%d (NPOT)", width, height, depth);
|
|
|
|
if (!caps.supportsTexture3D && type == Type_3D)
|
|
throw RBX::runtime_error("Unsupported type: 3D");
|
|
|
|
if (FFlag::GraphicsTextureCommitChanges && usage == Usage_Dynamic && isFormatCompressed(format))
|
|
throw RBX::runtime_error("Unsupported format: compressed formats are not supported for dynamic textures");
|
|
|
|
// createTexture poisons the binding on the first stage
|
|
static_cast<DeviceGL*>(device)->getImmediateContextGL()->invalidateCachedTextureStage(0);
|
|
|
|
id = createTexture(type, format, width, height, depth, mipLevels, caps);
|
|
|
|
if (FFlag::GraphicsTextureCommitChanges && usage == Usage_Dynamic && caps.ext3)
|
|
{
|
|
scratchSize = getTextureSize(type, format, width, height, depth, mipLevels);
|
|
|
|
glGenBuffers(1, &scratchId);
|
|
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, scratchId);
|
|
glBufferData(GL_PIXEL_UNPACK_BUFFER, scratchSize, NULL, GL_DYNAMIC_COPY);
|
|
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
|
}
|
|
}
|
|
|
|
TextureGL::TextureGL(Device* device, Type type, Format format, unsigned int width, unsigned int height, unsigned int depth, unsigned int mipLevels, Usage usage, unsigned int id)
|
|
: Texture(device, type, format, width, height, depth, mipLevels, usage)
|
|
, cachedState(SamplerState::Filter_Count) // initialize state to invalid value to force a cache miss on the next setup
|
|
, id(id)
|
|
, external(true)
|
|
, scratchId(0)
|
|
, scratchOffset(0)
|
|
, scratchSize(0)
|
|
{
|
|
}
|
|
|
|
TextureGL::~TextureGL()
|
|
{
|
|
static_cast<DeviceGL*>(device)->getImmediateContextGL()->invalidateCachedTexture(this);
|
|
|
|
if (scratchId)
|
|
glDeleteBuffers(1, &scratchId);
|
|
|
|
if (!external)
|
|
glDeleteTextures(1, &id);
|
|
}
|
|
|
|
void TextureGL::upload(unsigned int index, unsigned int mip, const TextureRegion& region, const void* data, unsigned int size)
|
|
{
|
|
const DeviceCapsGL& caps = static_cast<DeviceGL*>(device)->getCapsGL();
|
|
|
|
RBXASSERT(index < (type == Type_Cube ? 6u : 1u));
|
|
|
|
unsigned int mipWidth = getMipSide(width, mip);
|
|
unsigned int mipHeight = getMipSide(height, mip);
|
|
unsigned int mipDepth = getMipSide(depth, mip);
|
|
|
|
RBXASSERT(mip < mipLevels);
|
|
RBXASSERT(region.x + region.width <= mipWidth);
|
|
RBXASSERT(region.y + region.height <= mipHeight);
|
|
RBXASSERT(region.z + region.depth <= mipDepth);
|
|
|
|
RBXASSERT(size == getImageSize(format, region.width, region.height) * region.depth);
|
|
|
|
if (supportsLocking())
|
|
{
|
|
unsigned int bpp = getFormatBits(format) / 8;
|
|
|
|
LockResult lr = lock(index, mip, region);
|
|
|
|
for (unsigned int z = 0; z < region.depth; ++z)
|
|
for (unsigned int y = 0; y < region.height; ++y)
|
|
{
|
|
unsigned int sourceOffset = (region.width * region.height * z + region.width * y) * bpp;
|
|
unsigned int targetOffset = lr.slicePitch * z + lr.rowPitch * y;
|
|
|
|
memcpy(static_cast<char*>(lr.data) + targetOffset, static_cast<const char*>(data) + sourceOffset, region.width * bpp);
|
|
}
|
|
|
|
unlock(index, mip);
|
|
}
|
|
else
|
|
{
|
|
// uploadTexture poisons the binding on the first stage
|
|
static_cast<DeviceGL*>(device)->getImmediateContextGL()->invalidateCachedTextureStage(0);
|
|
|
|
bool entireRegion = (region.x == 0 && region.y == 0 && region.width == mipWidth && region.height == mipHeight);
|
|
|
|
uploadTexture(id, type, format, index, mip, region, entireRegion, data, caps);
|
|
}
|
|
}
|
|
|
|
#ifdef GLES
|
|
bool TextureGL::download(unsigned int index, unsigned int mip, void* data, unsigned int size)
|
|
{
|
|
return false;
|
|
}
|
|
#else
|
|
bool TextureGL::download(unsigned int index, unsigned int mip, void* data, unsigned int size)
|
|
{
|
|
const DeviceCapsGL& caps = static_cast<DeviceGL*>(device)->getCapsGL();
|
|
|
|
GLenum target = gTextureTargetGL[type];
|
|
const TextureFormatGL& formatGl = getTextureFormatGL(format, caps.ext3);
|
|
|
|
RBXASSERT(index < (type == Type_Cube ? 6u : 1u));
|
|
GLenum faceTarget = (type == Type_Cube) ? GL_TEXTURE_CUBE_MAP_POSITIVE_X + index : target;
|
|
|
|
unsigned int mipWidth = getMipSide(width, mip);
|
|
unsigned int mipHeight = getMipSide(height, mip);
|
|
unsigned int mipDepth = getMipSide(depth, mip);
|
|
|
|
RBXASSERT(mip < mipLevels);
|
|
RBXASSERT(size == getImageSize(format, mipWidth, mipHeight) * mipDepth);
|
|
|
|
static_cast<DeviceGL*>(device)->getImmediateContextGL()->invalidateCachedTextureStage(0);
|
|
|
|
glActiveTexture(GL_TEXTURE0);
|
|
glBindTexture(target, id);
|
|
|
|
bool needsCustomAlignment = (!isFormatCompressed(format) && getFormatBits(format) < 32);
|
|
|
|
glPixelStorei(GL_PACK_ALIGNMENT, needsCustomAlignment ? 1 : 4);
|
|
|
|
if (isFormatCompressed(format))
|
|
glGetCompressedTexImage(faceTarget, mip, data);
|
|
else
|
|
glGetTexImage(faceTarget, mip, formatGl.dataFormat, formatGl.dataType, data);
|
|
|
|
glBindTexture(target, 0);
|
|
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
bool TextureGL::supportsLocking() const
|
|
{
|
|
if (FFlag::GraphicsTextureCommitChanges)
|
|
return scratchId != 0;
|
|
|
|
return false;
|
|
}
|
|
|
|
Texture::LockResult TextureGL::lock(unsigned int index, unsigned int mip, const TextureRegion& region)
|
|
{
|
|
if (!scratchId)
|
|
{
|
|
Texture::LockResult result = {0, 0, 0};
|
|
|
|
return result;
|
|
}
|
|
|
|
RBXASSERT(index < (type == Type_Cube ? 6u : 1u));
|
|
|
|
unsigned int mipWidth = getMipSide(width, mip);
|
|
unsigned int mipHeight = getMipSide(height, mip);
|
|
unsigned int mipDepth = getMipSide(depth, mip);
|
|
|
|
RBXASSERT(mip < mipLevels);
|
|
RBXASSERT(region.x + region.width <= mipWidth);
|
|
RBXASSERT(region.y + region.height <= mipHeight);
|
|
RBXASSERT(region.z + region.depth <= mipDepth);
|
|
|
|
unsigned int size = getImageSize(format, region.width, region.height) * region.depth;
|
|
unsigned int flags = GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT;
|
|
|
|
if (scratchOffset + size > scratchSize)
|
|
{
|
|
commitChanges();
|
|
|
|
scratchOffset = 0;
|
|
flags = GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT;
|
|
}
|
|
|
|
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, scratchId);
|
|
void* data = glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, scratchOffset, size, flags);
|
|
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
|
|
|
Change change = { index, mip, region, scratchOffset };
|
|
|
|
scratchOffset += size;
|
|
pendingChanges.push_back(change);
|
|
|
|
Texture::LockResult result = {data, getImageSize(format, region.width, 1), getImageSize(format, region.width, region.height) };
|
|
|
|
return result;
|
|
}
|
|
|
|
void TextureGL::unlock(unsigned int index, unsigned int mip)
|
|
{
|
|
if (!scratchId)
|
|
return;
|
|
|
|
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, scratchId);
|
|
glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
|
|
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
|
}
|
|
|
|
shared_ptr<Renderbuffer> TextureGL::getRenderbuffer(unsigned int index, unsigned int mip)
|
|
{
|
|
RBXASSERT(usage == Usage_Renderbuffer);
|
|
|
|
RBXASSERT(mip == 0);
|
|
RBXASSERT(index < (type == Type_Cube ? 6u : 1u));
|
|
|
|
weak_ptr<Renderbuffer>& slot = renderBuffers[std::make_pair(index,mip)];
|
|
shared_ptr<Renderbuffer> rb = slot.lock();
|
|
|
|
if (!rb)
|
|
{
|
|
GLenum target = (type == Texture::Type_Cube) ? GL_TEXTURE_CUBE_MAP_POSITIVE_X + index : gTextureTargetGL[type];
|
|
|
|
rb.reset(new RenderbufferGL(device, shared_from_this(), target));
|
|
slot = rb;
|
|
}
|
|
|
|
return rb;
|
|
}
|
|
|
|
void TextureGL::commitChanges()
|
|
{
|
|
if (!FFlag::GraphicsTextureCommitChanges)
|
|
return;
|
|
|
|
const DeviceCapsGL& caps = static_cast<DeviceGL*>(device)->getCapsGL();
|
|
|
|
if (pendingChanges.empty())
|
|
return;
|
|
|
|
// uploadTexture poisons the binding on the first stage
|
|
static_cast<DeviceGL*>(device)->getImmediateContextGL()->invalidateCachedTextureStage(0);
|
|
|
|
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, scratchId);
|
|
|
|
for (auto& change: pendingChanges)
|
|
{
|
|
RBXASSERT(change.scratchOffset < scratchOffset);
|
|
|
|
bool entireRegion = false; // this is only important for compressed formats that we don't support
|
|
|
|
uploadTexture(id, type, format, change.index, change.mip, change.region, entireRegion, reinterpret_cast<void*>(change.scratchOffset), caps);
|
|
}
|
|
|
|
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
|
|
|
pendingChanges.clear();
|
|
}
|
|
|
|
void TextureGL::generateMipmaps()
|
|
{
|
|
RBXASSERT(usage != Usage_Dynamic);
|
|
|
|
if (mipLevels <= 1)
|
|
return;
|
|
|
|
static_cast<DeviceGL*>(device)->getImmediateContextGL()->invalidateCachedTextureStage(0);
|
|
|
|
glActiveTexture(GL_TEXTURE0);
|
|
glBindTexture(gTextureTargetGL[type], id);
|
|
|
|
glGenerateMipmap(gTextureTargetGL[type]);
|
|
|
|
glBindTexture(gTextureTargetGL[type], 0);
|
|
}
|
|
|
|
void TextureGL::bind(unsigned int stage, const SamplerState& state, TextureGL** cache)
|
|
{
|
|
if (*cache != this || cachedState != state)
|
|
{
|
|
glActiveTexture(GL_TEXTURE0 + stage);
|
|
|
|
GLenum target = gTextureTargetGL[type];
|
|
|
|
if (*cache != this)
|
|
{
|
|
*cache = this;
|
|
|
|
glBindTexture(target, id);
|
|
}
|
|
|
|
if (cachedState != state)
|
|
{
|
|
cachedState = state;
|
|
|
|
bool hasMips = mipLevels > 1;
|
|
|
|
glTexParameteri(target, GL_TEXTURE_MIN_FILTER, gSamplerFilterMinGL[state.getFilter()][hasMips]);
|
|
glTexParameteri(target, GL_TEXTURE_MAG_FILTER, gSamplerFilterMagGL[state.getFilter()]);
|
|
|
|
glTexParameteri(target, GL_TEXTURE_WRAP_S, gSamplerAddressGL[state.getAddress()]);
|
|
glTexParameteri(target, GL_TEXTURE_WRAP_T, gSamplerAddressGL[state.getAddress()]);
|
|
|
|
#ifndef GLES
|
|
int anisotropy = state.getFilter() == SamplerState::Filter_Anisotropic ? state.getAnisotropy() : 1;
|
|
|
|
glTexParameterf(target, GL_TEXTURE_MAX_ANISOTROPY_EXT, static_cast<float>(anisotropy));
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
unsigned int TextureGL::getTarget() const
|
|
{
|
|
return gTextureTargetGL[type];
|
|
}
|
|
|
|
unsigned int TextureGL::getInternalFormat(Format format)
|
|
{
|
|
return gTextureFormatGL[format].internalFormat;
|
|
}
|
|
|
|
}
|
|
}
|