From 63acd285a12691ce5d8aecb90a67ab0a9e21ccae Mon Sep 17 00:00:00 2001 From: meag Date: Fri, 12 Feb 2021 12:22:29 +0000 Subject: [PATCH] MODERN: Error handling for texture arrays NVIDIA driver was returning 2 errors in 32-bit Splits 3d-sprite arrays by size Error detection moved back to the texture array logic, as we need to reduce size in way that won't break lumas --- gl_texture.c | 33 ++++----- gl_texture.h | 2 +- glm_texture_arrays.c | 172 +++++++++++++++++++++++++------------------ r_texture.c | 20 ++--- r_texture.h | 2 +- 5 files changed, 129 insertions(+), 100 deletions(-) diff --git a/gl_texture.c b/gl_texture.c index 45251e523..1b88deac0 100644 --- a/gl_texture.c +++ b/gl_texture.c @@ -234,37 +234,32 @@ void GL_AllocateStorage(gltexture_t* texture) } #ifdef RENDERER_OPTION_MODERN_OPENGL -qbool GLM_TextureAllocateArrayStorage(gltexture_t* slot, int minimum_depth, int* depth) +qbool GLM_TextureAllocateArrayStorage(gltexture_t* slot) { GLenum error; - while (*depth >= minimum_depth) { - R_TraceAPI("Allocating %d x %d x %d, %d miplevels\n", slot->texture_width, slot->texture_height, *depth, slot->miplevels); - error = GL_TexStorage3D(GL_TEXTURE0, slot->reference, slot->miplevels, GL_StorageFormat(TEX_ALPHA), slot->texture_width, slot->texture_height, *depth, false); + R_TraceAPI("Allocating %d x %d x %d, %d miplevels\n", slot->texture_width, slot->texture_height, slot->depth, slot->miplevels); + GL_ProcessErrors("GLM_TextureAllocateArrayStorage flush"); + error = GL_TexStorage3D(GL_TEXTURE0, slot->reference, slot->miplevels, GL_StorageFormat(TEX_ALPHA), slot->texture_width, slot->texture_height, slot->depth, false); - if (error == GL_OUT_OF_MEMORY && *depth > 2) { - *depth /= 2; - R_TraceAPI("!!ERROR Array allocation failed (memory), reducing size to %d...\n", *depth); - continue; - } - else if (error != GL_NO_ERROR) { + if (error != GL_NO_ERROR) { #ifdef WITH_RENDERING_TRACE - int array_width, array_height, array_depth; - array_width = R_TextureWidth(slot->reference); - array_height = R_TextureHeight(slot->reference); - array_depth = R_TextureDepth(slot->reference); + int array_width, array_height, array_depth; + + array_width = R_TextureWidth(slot->reference); + array_height = R_TextureHeight(slot->reference); + array_depth = R_TextureDepth(slot->reference); - R_TraceAPI("!!ERROR Array allocation failed, error %X: [mip %d, %d x %d x %d]\n", error, slot->miplevels, slot->texture_width, slot->texture_height, *depth); - R_TraceAPI(" > Sizes reported: %d x %d x %d\n", array_width, array_height, array_depth); + R_TraceAPI("!!ERROR Array allocation failed, error %X: [mip %d, %d x %d x %d]\n", error, slot->miplevels, slot->texture_width, slot->texture_height, slot->depth); + R_TraceAPI(" > Sizes reported: %d x %d x %d\n", array_width, array_height, array_depth); #endif - return false; - } - break; + return false; } #ifdef DEBUG_MEMORY_ALLOCATIONS Sys_Printf("\nopengl-texture,alloc,%u,%d,%d,%d,%s\n", slot->reference.index, slot->texture_width, slot->texture_height, slot->texture_width * slot->texture_height * slot->depth * (slot->texmode & TEX_ALPHA ? 4 : 3), slot->identifier); #endif + return true; } #endif // RENDERER_OPTION_MODERN_OPENGL diff --git a/gl_texture.h b/gl_texture.h index 7712c2143..d59a9fbc0 100644 --- a/gl_texture.h +++ b/gl_texture.h @@ -33,7 +33,7 @@ void GL_UploadTexture(texture_ref texture, int mode, int width, int height, byte void GL_ReplaceSubImageRGBA(texture_ref ref, int offsetx, int offsety, int width, int height, byte* buffer); // Internal -qbool GLM_TextureAllocateArrayStorage(gltexture_t* slot, int minimum_depth, int* depth); +qbool GLM_TextureAllocateArrayStorage(gltexture_t* slot); void GL_AllocateStorage(gltexture_t* texture); void GL_AllocateTextureNames(gltexture_t* glt); diff --git a/glm_texture_arrays.c b/glm_texture_arrays.c index bd209a727..0e7cbf91f 100644 --- a/glm_texture_arrays.c +++ b/glm_texture_arrays.c @@ -442,10 +442,102 @@ static void GL_ImportTexturesForModel(model_t* mod) } } +static int GLM_FindPotentialSizes(int i, int* potential_sizes, int flagged_type, qbool maximise, int* req_width, int* req_height) +{ + int size_index = 0; + int j; + int width = R_TextureWidth(texture_flags[i].ref); + int height = R_TextureHeight(texture_flags[i].ref); + + potential_sizes[size_index++] = 1 + texture_flags[i].subsequent; + + // Count how many textures of the same size we have + for (j = i + 1; j < MAX_GLTEXTURES; ++j) { + if (!(texture_flags[j].flags & (1 << flagged_type))) { + break; + } + + if (maximise) { + width = max(width, R_TextureWidth(texture_flags[j].ref)); + height = max(height, R_TextureHeight(texture_flags[j].ref)); + } + else if (R_TextureWidth(texture_flags[j].ref) != width || R_TextureHeight(texture_flags[j].ref) != height) { + break; + } + + potential_sizes[size_index] = potential_sizes[size_index - 1] + 1 + texture_flags[j].subsequent; + ++size_index; + } + + *req_width = width; + *req_height = height; + return size_index; +} + +static void GLM_CopyTexturesToArray(texture_ref array_ref, int flagged_type, int min_index, int max_index, int width, int height) +{ + int array_index = 0; + int k; + + // texture created ok + if (flagged_type == TEXTURETYPES_SPRITES) { + renderer.TextureWrapModeClamp(array_ref); + } + + // Copy the 2D textures across + for (k = min_index; k <= max_index; ++k) { + texture_ref ref_2d = texture_flags[k].ref; + + // TODO: compression: flag as ANYSIZE and set scale_s/scale_t accordingly + GL_AddTextureToArray(array_ref, array_index, ref_2d, false); + texture_flags[k].array_ref[flagged_type].ref = array_ref; + texture_flags[k].array_ref[flagged_type].index = array_index; + texture_flags[k].array_ref[flagged_type].scale_s = (R_TextureWidth(ref_2d) * 1.0f) / width; + texture_flags[k].array_ref[flagged_type].scale_t = (R_TextureHeight(ref_2d) * 1.0f) / height; + + // (subsequent are the luma textures on brush models) + array_index += 1 + texture_flags[k].subsequent; + } +} + +static int GLM_CreateArrayFromPotentialSizes(int i, int* potential_sizes, int size_index_max, qbool return_on_failure, int width, int height) +{ + int size_attempt, depth; + + const char* textureTypeNames[] = { + "aliasmodel", + "brushmodel", + "worldmodel", + "sprites", + }; + + for (size_attempt = size_index_max - 1; size_attempt >= 0; --size_attempt) { + // create array of desired size + char name[64]; + texture_ref array_ref; + + R_TextureReferenceInvalidate(array_ref); + depth = potential_sizes[size_attempt]; + snprintf(name, sizeof(name), "%s_%d[%dx%dx%d]", textureTypeNames[flagged_type], i, width, height, depth); + + array_ref = R_CreateTextureArray(name, width, height, depth, TEX_MIPMAP | TEX_ALPHA); + if (R_TextureReferenceIsValid(array_ref)) { + GLM_CopyTexturesToArray(array_ref, flagged_type, i, i + size_attempt, width, height); + return size_attempt; + } + } + + if (!return_on_failure) { + Sys_Error("Failed to create array size %dx%dx%d\n", width, height, potential_sizes[size_index_max - 1]); + } + return -1; +} + // Called from R_NewMap void GLM_BuildCommonTextureArrays(qbool vid_restart) { int i; + int potential_sizes[MAX_GLTEXTURES]; GL_DeleteExistingTextureArrays(!vid_restart); R_ClearModelTextureData(); @@ -491,82 +583,22 @@ void GLM_BuildCommonTextureArrays(qbool vid_restart) qsort(texture_flags, MAX_GLTEXTURES, sizeof(texture_flags[0]), SortFlaggedTextures); for (i = 0; i < MAX_GLTEXTURES; ++i) { - int same_size = 0; - int width, height; - int j; - int depth; + int size_index_max = 0; + int extra_slots_added; + int req_width, req_height; if (!(texture_flags[i].flags & (1 << flagged_type))) { continue; } - width = R_TextureWidth(texture_flags[i].ref); - height = R_TextureHeight(texture_flags[i].ref); - same_size = 1 + texture_flags[i].subsequent; - - // Count how many textures of the same size we have - for (j = i + 1; j < MAX_GLTEXTURES; ++j) { - if (!(texture_flags[j].flags & (1 << flagged_type))) { - break; - } - - if (flagged_type == TEXTURETYPES_SPRITES) { - width = max(width, R_TextureWidth(texture_flags[j].ref)); - height = max(height, R_TextureHeight(texture_flags[j].ref)); - } - else if (R_TextureWidth(texture_flags[j].ref) != width || R_TextureHeight(texture_flags[j].ref) != height) { - break; - } - - same_size += 1 + texture_flags[j].subsequent; - } - - // Create an array of that size - while (same_size > 0) { - texture_ref array_ref; - int index = 0; - int k; - char name[64]; - const char* textureTypeNames[] = { - "aliasmodel", - "brushmodel", - "worldmodel", - "sprites", - }; - - snprintf(name, sizeof(name), "%s[%dx%dx%d]", textureTypeNames[flagged_type], width, height, same_size); - - depth = same_size; - array_ref = R_CreateTextureArray(name, width, height, &depth, TEX_MIPMAP | TEX_ALPHA, 1); - if (!R_TextureReferenceIsValid(array_ref)) { - Sys_Error("Failed to create array size %dx%dx%d\n", width, height, depth); - } - - if (flagged_type == TEXTURETYPES_SPRITES) { - renderer.TextureWrapModeClamp(array_ref); - } - - // Copy the 2D textures across - for (k = i; k < j; ++k) { - texture_ref ref_2d = texture_flags[k].ref; - - // TODO: compression: flag as ANYSIZE and set scale_s/scale_t accordingly - GL_AddTextureToArray(array_ref, index, ref_2d, false); - texture_flags[k].array_ref[flagged_type].ref = array_ref; - texture_flags[k].array_ref[flagged_type].index = index++; - texture_flags[k].array_ref[flagged_type].scale_s = (R_TextureWidth(ref_2d) * 1.0f) / width; - texture_flags[k].array_ref[flagged_type].scale_t = (R_TextureHeight(ref_2d) * 1.0f) / height; - - // Skip these and fill them in later - index += texture_flags[k].subsequent; - } - - //R_GenerateMipmapsIfNeeded(array_ref); - same_size -= depth; - i = j; + size_index_max = GLM_FindPotentialSizes(i, potential_sizes, flagged_type, flagged_type == TEXTURETYPES_SPRITES, &req_width, &req_height); + extra_slots_added = GLM_CreateArrayFromPotentialSizes(i, potential_sizes, size_index_max, flagged_type == TEXTURETYPES_SPRITES, req_width, req_height); + if (extra_slots_added == -1) { + // try again without maximising + GLM_FindPotentialSizes(i, potential_sizes, flagged_type, false, &req_width, &req_height); + extra_slots_added = GLM_CreateArrayFromPotentialSizes(i, potential_sizes, size_index_max, false, req_width, req_height); } - - i--; + i += extra_slots_added; } } diff --git a/r_texture.c b/r_texture.c index 307c847a9..5423ee619 100644 --- a/r_texture.c +++ b/r_texture.c @@ -492,12 +492,13 @@ void R_SetTextureArraySize(texture_ref tex, int width, int height, int depth, in // We could flag the textures as they're created and then move all 2d>3d to this module? // FIXME: Ensure not called in immediate-mode OpenGL -texture_ref R_CreateTextureArray(const char* identifier, int width, int height, int* depth, int mode, int minimum_depth) +texture_ref R_CreateTextureArray(const char* identifier, int width, int height, int depth, int mode) { qbool new_texture = false; - gltexture_t* slot = R_TextureAllocateSlot(texture_type_2d_array, identifier, width, height, *depth, 4, mode | TEX_NOSCALE, 0, &new_texture); + gltexture_t* slot; texture_ref gl_texturenum; + slot = R_TextureAllocateSlot(texture_type_2d_array, identifier, width, height, depth, 4, mode | TEX_NOSCALE, 0, &new_texture); if (!slot) { return invalid_texture_reference; } @@ -510,15 +511,16 @@ texture_ref R_CreateTextureArray(const char* identifier, int width, int height, #ifdef RENDERER_OPTION_MODERN_OPENGL if (R_UseModernOpenGL()) { - renderer.TextureUnitBind(0, gl_texturenum); - if (GLM_TextureAllocateArrayStorage(slot, minimum_depth, depth)) { + texture_ref tex = slot->reference; + + renderer.TextureUnitBind(0, tex); + if (GLM_TextureAllocateArrayStorage(slot)) { R_TextureUtil_SetFiltering(slot->reference); + return tex; } - else { - texture_ref tex = slot->reference; - R_DeleteTexture(&tex); - return null_texture_reference; - } + + R_DeleteTexture(&tex); + return null_texture_reference; } #endif #ifdef RENDERER_OPTION_VULKAN diff --git a/r_texture.h b/r_texture.h index 505054132..4664e7dfc 100644 --- a/r_texture.h +++ b/r_texture.h @@ -103,7 +103,7 @@ texture_ref R_LoadTexture(const char *identifier, int width, int height, byte *d texture_ref R_LoadPicTexture(const char *name, mpic_t *pic, byte *data); texture_ref R_LoadTexturePixels(byte *data, const char *identifier, int width, int height, int mode); texture_ref R_LoadTextureImage(const char *filename, const char *identifier, int matchwidth, int matchheight, int mode); -texture_ref R_CreateTextureArray(const char* identifier, int width, int height, int* depth, int mode, int minimum_depth); +texture_ref R_CreateTextureArray(const char* identifier, int width, int height, int depth, int mode); texture_ref R_CreateCubeMap(const char* identifier, int width, int height, int mode); void R_DeleteTextureArray(texture_ref* texture); void R_DeleteCubeMap(texture_ref* texture);