Implement 3D texturing in the OpenGL backend too. Assorted fixes.

This commit is contained in:
Henrik Rydgård 2022-07-30 21:33:24 +02:00
parent 566385f762
commit fecf9127a0
15 changed files with 128 additions and 62 deletions

View File

@ -59,6 +59,7 @@ void DirectXState::Restore() {
texMaxMipLevel.restore(); count++;
texAddressU.restore(); count++;
texAddressV.restore(); count++;
texAddressW.restore(); count++;
}
} // namespace DX9

View File

@ -393,6 +393,7 @@ public:
DxSampler0State1<D3DSAMP_MAXMIPLEVEL, 0> texMaxMipLevel;
DxSampler0State1<D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP> texAddressU;
DxSampler0State1<D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP> texAddressV;
DxSampler0State1<D3DSAMP_ADDRESSW, D3DTADDRESS_CLAMP> texAddressW;
};
#undef STATE1

View File

@ -348,7 +348,17 @@ void GLQueueRunner::RunInitSteps(const std::vector<GLRInitStep> &steps, bool ski
GLenum internalFormat, format, type;
int alignment;
Thin3DFormatToFormatAndType(step.texture_image.format, internalFormat, format, type, alignment);
glTexImage2D(tex->target, step.texture_image.level, internalFormat, step.texture_image.width, step.texture_image.height, 0, format, type, step.texture_image.data);
if (step.texture_image.depth == 1) {
glTexImage2D(tex->target,
step.texture_image.level, internalFormat,
step.texture_image.width, step.texture_image.height, 0,
format, type, step.texture_image.data);
} else {
glTexImage3D(tex->target,
step.texture_image.level, internalFormat,
step.texture_image.width, step.texture_image.height, step.texture_image.depth, 0,
format, type, step.texture_image.data);
}
allocatedTextures = true;
if (step.texture_image.allocType == GLRAllocType::ALIGNED) {
FreeAlignedMemory(step.texture_image.data);
@ -364,6 +374,9 @@ void GLQueueRunner::RunInitSteps(const std::vector<GLRInitStep> &steps, bool ski
glTexParameteri(tex->target, GL_TEXTURE_WRAP_T, tex->wrapT);
glTexParameteri(tex->target, GL_TEXTURE_MAG_FILTER, tex->magFilter);
glTexParameteri(tex->target, GL_TEXTURE_MIN_FILTER, tex->minFilter);
if (step.texture_image.depth > 1) {
glTexParameteri(tex->target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
}
CHECK_GL_ERROR_IF_DEBUG();
break;
}
@ -375,7 +388,7 @@ void GLQueueRunner::RunInitSteps(const std::vector<GLRInitStep> &steps, bool ski
glBindTexture(tex->target, tex->texture);
boundTexture = tex->texture;
}
if (!gl_extensions.IsGLES || gl_extensions.GLES3) {
if ((!gl_extensions.IsGLES || gl_extensions.GLES3) && step.texture_finalize.loadedLevels > 1) {
glTexParameteri(tex->target, GL_TEXTURE_MAX_LEVEL, step.texture_finalize.loadedLevels - 1);
}
tex->maxLod = (float)step.texture_finalize.loadedLevels - 1;
@ -1139,28 +1152,28 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step, bool first, bool last
CHECK_GL_ERROR_IF_DEBUG();
if (tex->canWrap) {
if (tex->wrapS != c.textureSampler.wrapS) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, c.textureSampler.wrapS);
glTexParameteri(tex->target, GL_TEXTURE_WRAP_S, c.textureSampler.wrapS);
tex->wrapS = c.textureSampler.wrapS;
}
if (tex->wrapT != c.textureSampler.wrapT) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, c.textureSampler.wrapT);
glTexParameteri(tex->target, GL_TEXTURE_WRAP_T, c.textureSampler.wrapT);
tex->wrapT = c.textureSampler.wrapT;
}
}
CHECK_GL_ERROR_IF_DEBUG();
if (tex->magFilter != c.textureSampler.magFilter) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, c.textureSampler.magFilter);
glTexParameteri(tex->target, GL_TEXTURE_MAG_FILTER, c.textureSampler.magFilter);
tex->magFilter = c.textureSampler.magFilter;
}
CHECK_GL_ERROR_IF_DEBUG();
if (tex->minFilter != c.textureSampler.minFilter) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, c.textureSampler.minFilter);
glTexParameteri(tex->target, GL_TEXTURE_MIN_FILTER, c.textureSampler.minFilter);
tex->minFilter = c.textureSampler.minFilter;
}
CHECK_GL_ERROR_IF_DEBUG();
if (tex->anisotropy != c.textureSampler.anisotropy) {
if (c.textureSampler.anisotropy != 0.0f) {
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, c.textureSampler.anisotropy);
glTexParameterf(tex->target, GL_TEXTURE_MAX_ANISOTROPY_EXT, c.textureSampler.anisotropy);
}
tex->anisotropy = c.textureSampler.anisotropy;
}
@ -1180,16 +1193,16 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step, bool first, bool last
}
#ifndef USING_GLES2
if (tex->lodBias != c.textureLod.lodBias && !gl_extensions.IsGLES) {
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, c.textureLod.lodBias);
glTexParameterf(tex->target, GL_TEXTURE_LOD_BIAS, c.textureLod.lodBias);
tex->lodBias = c.textureLod.lodBias;
}
#endif
if (tex->minLod != c.textureLod.minLod) {
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_LOD, c.textureLod.minLod);
glTexParameterf(tex->target, GL_TEXTURE_MIN_LOD, c.textureLod.minLod);
tex->minLod = c.textureLod.minLod;
}
if (tex->maxLod != c.textureLod.maxLod) {
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_LOD, c.textureLod.maxLod);
glTexParameterf(tex->target, GL_TEXTURE_MAX_LOD, c.textureLod.maxLod);
tex->maxLod = c.textureLod.maxLod;
}
break;
@ -1200,6 +1213,7 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step, bool first, bool last
// TODO: Need bind?
if (!c.texture_subimage.data)
Crash();
_assert_(tex->target == GL_TEXTURE_2D);
// For things to show in RenderDoc, need to split into glTexImage2D(..., nullptr) and glTexSubImage.
GLuint internalFormat, format, type;
int alignment;

View File

@ -259,8 +259,9 @@ struct GLRInitStep {
GLRTexture *texture;
Draw::DataFormat format;
int level;
int width;
int height;
uint16_t width;
uint16_t height;
uint16_t depth;
GLRAllocType allocType;
bool linearFilter;
uint8_t *data; // owned, delete[]-d

View File

@ -21,7 +21,7 @@ static bool OnRenderThread() {
}
#endif
GLRTexture::GLRTexture(int width, int height, int numMips) {
GLRTexture::GLRTexture(int width, int height, int depth, int numMips) {
if (gl_extensions.OES_texture_npot) {
canWrap = true;
} else {
@ -29,6 +29,7 @@ GLRTexture::GLRTexture(int width, int height, int numMips) {
}
w = width;
h = height;
depth = depth;
this->numMips = numMips;
}

View File

@ -25,12 +25,13 @@ constexpr int MAX_GL_TEXTURE_SLOTS = 8;
class GLRTexture {
public:
GLRTexture(int width, int height, int numMips);
GLRTexture(int width, int height, int depth, int numMips);
~GLRTexture();
GLuint texture = 0;
uint16_t w;
uint16_t h;
uint16_t d;
// We don't trust OpenGL defaults - setting wildly off values ensures that we'll end up overwriting these parameters.
GLenum target = 0xFFFF;
@ -49,7 +50,7 @@ public:
class GLRFramebuffer {
public:
GLRFramebuffer(int _width, int _height, bool z_stencil)
: color_texture(_width, _height, 1), z_stencil_texture(_width, _height, 1),
: color_texture(_width, _height, 1, 1), z_stencil_texture(_width, _height, 1, 1),
width(_width), height(_height), z_stencil_(z_stencil) {
}
@ -384,9 +385,9 @@ public:
// Creation commands. These were not needed in Vulkan since there we can do that on the main thread.
// We pass in width/height here even though it's not strictly needed until we support glTextureStorage
// and then we'll also need formats and stuff.
GLRTexture *CreateTexture(GLenum target, int width, int height, int numMips) {
GLRTexture *CreateTexture(GLenum target, int width, int height, int depth, int numMips) {
GLRInitStep step{ GLRInitStepType::CREATE_TEXTURE };
step.create_texture.texture = new GLRTexture(width, height, numMips);
step.create_texture.texture = new GLRTexture(width, height, depth, numMips);
step.create_texture.texture->target = target;
initSteps_.push_back(step);
return step.create_texture.texture;
@ -537,7 +538,7 @@ public:
}
// Takes ownership over the data pointer and delete[]-s it.
void TextureImage(GLRTexture *texture, int level, int width, int height, Draw::DataFormat format, uint8_t *data, GLRAllocType allocType = GLRAllocType::NEW, bool linearFilter = false) {
void TextureImage(GLRTexture *texture, int level, int width, int height, int depth, Draw::DataFormat format, uint8_t *data, GLRAllocType allocType = GLRAllocType::NEW, bool linearFilter = false) {
GLRInitStep step{ GLRInitStepType::TEXTURE_IMAGE };
step.texture_image.texture = texture;
step.texture_image.data = data;
@ -545,6 +546,7 @@ public:
step.texture_image.level = level;
step.texture_image.width = width;
step.texture_image.height = height;
step.texture_image.depth = depth;
step.texture_image.allocType = allocType;
step.texture_image.linearFilter = linearFilter;
initSteps_.push_back(step);

View File

@ -792,7 +792,7 @@ OpenGLTexture::OpenGLTexture(GLRenderManager *render, const TextureDesc &desc) :
format_ = desc.format;
type_ = desc.type;
GLenum target = TypeToTarget(desc.type);
tex_ = render->CreateTexture(target, desc.width, desc.height, desc.mipLevels);
tex_ = render->CreateTexture(target, desc.width, desc.height, 1, desc.mipLevels);
mipLevels_ = desc.mipLevels;
if (desc.initData.empty())
@ -877,7 +877,7 @@ void OpenGLTexture::SetImageData(int x, int y, int z, int width, int height, int
}
}
render_->TextureImage(tex_, level, width, height, format_, texData);
render_->TextureImage(tex_, level, width, height, depth, format_, texData);
}
#ifdef DEBUG_READ_PIXELS

View File

@ -276,8 +276,13 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu
WRITE(p, "precision highp int;\n");
}
if (doTexture)
WRITE(p, "uniform sampler2D tex;\n");
if (doTexture) {
if (texture3D) {
WRITE(p, "uniform sampler3D tex;\n");
} else {
WRITE(p, "uniform sampler2D tex;\n");
}
}
if (readFramebufferTex) {
if (!compat.texelFetch) {
@ -337,6 +342,11 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu
WRITE(p, "uniform vec3 u_texenv;\n");
}
if (texture3D) {
*uniformMask |= DIRTY_TEXCLAMP;
WRITE(p, "uniform float u_mipBias;\n");
}
WRITE(p, "%s %s lowp vec4 v_color0;\n", shading, compat.varying_fs);
if (lmode)
WRITE(p, "%s %s lowp vec3 v_color1;\n", shading, compat.varying_fs);

View File

@ -316,8 +316,9 @@ void ShaderManagerDX9::PSUpdateUniforms(u64 dirtyUniforms) {
PSSetFloatArray(CONST_PS_TEXCLAMPOFF, texclampoff, 2);
float mipBias = (float)gstate.getTexLevelOffset16() * (1.0 / 16.0f);
mipBias = (mipBias + 0.5f) / (float)(gstate.getTextureMaxLevel() + 1);
// NOTE: This equation needs some adjustment in D3D9. Can't get it to look completely smooth :(
mipBias = (mipBias + 0.25f) / (float)(gstate.getTextureMaxLevel() + 1);
PSSetFloatArray(CONST_PS_MIPBIAS, &mipBias, 1);
}
}

View File

@ -98,11 +98,11 @@ GLRTexture *DepalShaderCacheGLES::GetClutTexture(GEPaletteFormat clutFormat, con
int texturePixels = clutFormat == GE_CMODE_32BIT_ABGR8888 ? 256 : 512;
DepalTexture *tex = new DepalTexture();
tex->texture = render_->CreateTexture(GL_TEXTURE_2D, texturePixels, 1, 1);
tex->texture = render_->CreateTexture(GL_TEXTURE_2D, texturePixels, 1, 1, 1);
uint8_t *clutCopy = new uint8_t[1024];
memcpy(clutCopy, rawClut, 1024);
render_->TextureImage(tex->texture, 0, texturePixels, 1, dstFmt, clutCopy, GLRAllocType::NEW, false);
render_->TextureImage(tex->texture, 0, texturePixels, 1, 1, dstFmt, clutCopy, GLRAllocType::NEW, false);
tex->lastFrame = gpuStats.numFlips;
texCache_[clutId] = tex;

View File

@ -502,8 +502,8 @@ void TessellationDataTransferGLES::SendDataToShader(const SimpleVertex *const *p
prevSizeU = size_u;
prevSizeV = size_v;
if (!data_tex[0])
data_tex[0] = renderManager_->CreateTexture(GL_TEXTURE_2D, size_u * 3, size_v, 1);
renderManager_->TextureImage(data_tex[0], 0, size_u * 3, size_v, Draw::DataFormat::R32G32B32A32_FLOAT, nullptr, GLRAllocType::NONE, false);
data_tex[0] = renderManager_->CreateTexture(GL_TEXTURE_2D, size_u * 3, size_v, 1, 1);
renderManager_->TextureImage(data_tex[0], 0, size_u * 3, size_v, 1, Draw::DataFormat::R32G32B32A32_FLOAT, nullptr, GLRAllocType::NONE, false);
renderManager_->FinalizeTexture(data_tex[0], 0, false);
}
renderManager_->BindTexture(TEX_SLOT_SPLINE_POINTS, data_tex[0]);
@ -520,8 +520,8 @@ void TessellationDataTransferGLES::SendDataToShader(const SimpleVertex *const *p
if (prevSizeWU < weights.size_u) {
prevSizeWU = weights.size_u;
if (!data_tex[1])
data_tex[1] = renderManager_->CreateTexture(GL_TEXTURE_2D, weights.size_u * 2, 1, 1);
renderManager_->TextureImage(data_tex[1], 0, weights.size_u * 2, 1, Draw::DataFormat::R32G32B32A32_FLOAT, nullptr, GLRAllocType::NONE, false);
data_tex[1] = renderManager_->CreateTexture(GL_TEXTURE_2D, weights.size_u * 2, 1, 1, 1);
renderManager_->TextureImage(data_tex[1], 0, weights.size_u * 2, 1, 1, Draw::DataFormat::R32G32B32A32_FLOAT, nullptr, GLRAllocType::NONE, false);
renderManager_->FinalizeTexture(data_tex[1], 0, false);
}
renderManager_->BindTexture(TEX_SLOT_SPLINE_WEIGHTS_U, data_tex[1]);
@ -531,8 +531,8 @@ void TessellationDataTransferGLES::SendDataToShader(const SimpleVertex *const *p
if (prevSizeWV < weights.size_v) {
prevSizeWV = weights.size_v;
if (!data_tex[2])
data_tex[2] = renderManager_->CreateTexture(GL_TEXTURE_2D, weights.size_v * 2, 1, 1);
renderManager_->TextureImage(data_tex[2], 0, weights.size_v * 2, 1, Draw::DataFormat::R32G32B32A32_FLOAT, nullptr, GLRAllocType::NONE, false);
data_tex[2] = renderManager_->CreateTexture(GL_TEXTURE_2D, weights.size_v * 2, 1, 1, 1);
renderManager_->TextureImage(data_tex[2], 0, weights.size_v * 2, 1, 1, Draw::DataFormat::R32G32B32A32_FLOAT, nullptr, GLRAllocType::NONE, false);
renderManager_->FinalizeTexture(data_tex[2], 0, false);
}
renderManager_->BindTexture(TEX_SLOT_SPLINE_WEIGHTS_V, data_tex[2]);

View File

@ -144,8 +144,8 @@ GLRTexture *FragmentTestCacheGLES::CreateTestTexture(const GEComparison funcs[4]
}
}
GLRTexture *tex = render_->CreateTexture(GL_TEXTURE_2D, 256, 1, 1);
render_->TextureImage(tex, 0, 256, 1, Draw::DataFormat::R8G8B8A8_UNORM, data);
GLRTexture *tex = render_->CreateTexture(GL_TEXTURE_2D, 256, 1, 1, 1);
render_->TextureImage(tex, 0, 256, 1, 1, Draw::DataFormat::R8G8B8A8_UNORM, data);
return tex;
}

View File

@ -174,6 +174,7 @@ LinkedShader::LinkedShader(GLRenderManager *render, VShaderID VSID, Shader *vs,
queries.push_back({ &u_tess_weights_v, "u_tess_weights_v" });
queries.push_back({ &u_spline_counts, "u_spline_counts" });
queries.push_back({ &u_depal_mask_shift_off_fmt, "u_depal_mask_shift_off_fmt" });
queries.push_back({ &u_mipBias, "u_mipBias" });
attrMask = vs->GetAttrMask();
availableUniforms = vs->GetUniformMask() | fs->GetUniformMask();
@ -458,6 +459,13 @@ void LinkedShader::UpdateUniforms(u32 vertType, const ShaderID &vsid, bool useBu
}
}
if ((dirty & DIRTY_TEXCLAMP) && u_mipBias != -1) {
float mipBias = (float)gstate.getTexLevelOffset16() * (1.0 / 16.0f);
mipBias = (mipBias + 0.5f) / (float)(gstate.getTextureMaxLevel() + 1);
render_->SetUniformF(&u_mipBias, 1, &mipBias);
}
// Transform
if (dirty & DIRTY_WORLDMATRIX) {
SetMatrix4x3(render_, &u_world, gstate.worldMatrix);

View File

@ -61,6 +61,7 @@ public:
int u_cullRangeMin;
int u_cullRangeMax;
int u_rotation;
int u_mipBias;
#ifdef USE_BONE_ARRAY
int u_bone; // array, size is numBones

View File

@ -451,7 +451,11 @@ void TextureCacheGLES::BuildTexture(TexCacheEntry *const entry) {
dstFmt = Draw::DataFormat::R8G8B8A8_UNORM;
}
entry->textureName = render_->CreateTexture(GL_TEXTURE_2D, tw, tw, plan.levelsToCreate);
if (plan.depth == 1) {
entry->textureName = render_->CreateTexture(GL_TEXTURE_2D, tw, tw, 1, plan.levelsToCreate);
} else {
entry->textureName = render_->CreateTexture(GL_TEXTURE_3D, tw, tw, plan.depth, 1);
}
// Apply some additional compatibility checks.
if (plan.levelsToLoad > 1) {
@ -468,46 +472,68 @@ void TextureCacheGLES::BuildTexture(TexCacheEntry *const entry) {
plan.levelsToCreate = plan.levelsToLoad;
}
for (int i = 0; i < plan.levelsToLoad; i++) {
int srcLevel = i == 0 ? plan.baseLevelSrc : i;
if (plan.depth == 1) {
for (int i = 0; i < plan.levelsToLoad; i++) {
int srcLevel = i == 0 ? plan.baseLevelSrc : i;
int w = gstate.getTextureWidth(srcLevel);
int h = gstate.getTextureHeight(srcLevel);
int w = gstate.getTextureWidth(srcLevel);
int h = gstate.getTextureHeight(srcLevel);
u8 *data = nullptr;
int stride = 0;
u8 *data = nullptr;
int stride = 0;
if (plan.replaced->GetSize(srcLevel, w, h)) {
int bpp = (int)Draw::DataFormatSizeInBytes(plan.replaced->Format(srcLevel));
stride = w * bpp;
data = (u8 *)AllocateAlignedMemory(stride * h, 16);
} else {
if (plan.scaleFactor > 1) {
data = (u8 *)AllocateAlignedMemory(4 * (w * plan.scaleFactor) * (h * plan.scaleFactor), 16);
stride = w * plan.scaleFactor * 4;
} else {
int bpp = dstFmt == Draw::DataFormat::R8G8B8A8_UNORM ? 4 : 2;
stride = std::max(w * bpp, 4);
if (plan.replaced->GetSize(srcLevel, w, h)) {
int bpp = (int)Draw::DataFormatSizeInBytes(plan.replaced->Format(srcLevel));
stride = w * bpp;
data = (u8 *)AllocateAlignedMemory(stride * h, 16);
} else {
if (plan.scaleFactor > 1) {
data = (u8 *)AllocateAlignedMemory(4 * (w * plan.scaleFactor) * (h * plan.scaleFactor), 16);
stride = w * plan.scaleFactor * 4;
} else {
int bpp = dstFmt == Draw::DataFormat::R8G8B8A8_UNORM ? 4 : 2;
stride = std::max(w * bpp, 4);
data = (u8 *)AllocateAlignedMemory(stride * h, 16);
}
}
if (!data) {
ERROR_LOG(G3D, "Ran out of RAM trying to allocate a temporary texture upload buffer (%dx%d)", w, h);
return;
}
LoadTextureLevel(*entry, data, stride, *plan.replaced, srcLevel, plan.scaleFactor, dstFmt, true);
// NOTE: TextureImage takes ownership of data, so we don't free it afterwards.
render_->TextureImage(entry->textureName, i, w * plan.scaleFactor, h * plan.scaleFactor, 1, dstFmt, data, GLRAllocType::ALIGNED);
}
if (!data) {
ERROR_LOG(G3D, "Ran out of RAM trying to allocate a temporary texture upload buffer (%dx%d)", w, h);
return;
bool genMips = plan.levelsToCreate > plan.levelsToLoad;
render_->FinalizeTexture(entry->textureName, plan.levelsToLoad, genMips);
} else {
int bpp = dstFmt == Draw::DataFormat::R8G8B8A8_UNORM ? 4 : 2;
int stride = bpp * (plan.w * plan.scaleFactor);
int levelStride = stride * (plan.h * plan.scaleFactor);
u8 *data = (u8 *)AllocateAlignedMemory(levelStride * plan.depth, 16);
memset(data, 0, levelStride * plan.depth);
u8 *p = data;
for (int i = 0; i < plan.depth; i++) {
LoadTextureLevel(*entry, p, stride, *plan.replaced, i, plan.scaleFactor, dstFmt, true);
p += levelStride;
}
LoadTextureLevel(*entry, data, stride, *plan.replaced, srcLevel, plan.scaleFactor, dstFmt, true);
render_->TextureImage(entry->textureName, 0, plan.w * plan.scaleFactor, plan.h * plan.scaleFactor, plan.depth, dstFmt, data, GLRAllocType::ALIGNED);
// NOTE: TextureImage takes ownership of data, so we don't free it afterwards.
render_->TextureImage(entry->textureName, i, w * plan.scaleFactor, h * plan.scaleFactor, dstFmt, data, GLRAllocType::ALIGNED);
// Signal that we support depth textures so use it as one.
entry->status |= TexCacheEntry::STATUS_3D;
render_->FinalizeTexture(entry->textureName, 1, false);
}
bool genMips = plan.levelsToCreate > plan.levelsToLoad;
render_->FinalizeTexture(entry->textureName, plan.levelsToLoad, genMips);
if (plan.replaced->Valid()) {
entry->SetAlphaStatus(TexCacheEntry::TexStatus(plan.replaced->AlphaStatus()));
}