From 67307032da45e3b7b694984504bee145f12171fc Mon Sep 17 00:00:00 2001 From: Alberto Torres Date: Mon, 16 Dec 2024 21:05:21 +0100 Subject: [PATCH] Textures: Add several defines and constructor options for compression, cache. * Add defines `myouForceAstc`, `myouMinTextureChannels`, `myouLoadUncompressedTextures` and `myouAllCacheFilesExist`. * Suppress warning messages about emulation for `myouForceAstc`. * Add argument `use_compression` to `newTexture` (default `true`) --- src/gpu_formats/texture_decode.nim | 2 +- src/gpu_formats/texture_optimize.nim | 48 ++++++++++++++++++++-------- src/graphics/texture.nim | 20 +++++++++--- src/platform/glfw_wrap.nim | 6 ++++ 4 files changed, 57 insertions(+), 19 deletions(-) diff --git a/src/gpu_formats/texture_decode.nim b/src/gpu_formats/texture_decode.nim index ef81b31..40543a5 100644 --- a/src/gpu_formats/texture_decode.nim +++ b/src/gpu_formats/texture_decode.nim @@ -138,7 +138,7 @@ proc loadFileFromSlices*(tex: Texture, slices: seq[SliceMem[byte]], when not defined(nimdoc): assert tex.tex_type != TexCube, "Loading a cube texture from file is not supported yet" - let format = if min_channels == 0: + let format = if min_channels <= tex.format.channel_count: tex.format else: tex.format.resize(min_channels) diff --git a/src/gpu_formats/texture_optimize.nim b/src/gpu_formats/texture_optimize.nim index 37edb77..5fb8766 100644 --- a/src/gpu_formats/texture_optimize.nim +++ b/src/gpu_formats/texture_optimize.nim @@ -68,14 +68,22 @@ when defined(nimdoc): type TYPES* = CacheSettings | EncodingSpeed | RgbBcFmt | BlockSize when defined(android) or defined(ios) or defined(emscripten): - template has_bptc_support: bool = gl.GLAD_GL_EXT_texture_compression_bptc + template has_bptc_support: bool = gl.GLAD_GL_EXT_texture_compression_bptc and not defined(myouForceAstc) else: - template has_bptc_support: bool = gl.GLAD_GL_ARB_texture_compression_bptc + template has_bptc_support: bool = gl.GLAD_GL_ARB_texture_compression_bptc and not defined(myouForceAstc) template has_astc_support: bool = gl.GLAD_GL_OES_texture_compression_astc or - gl.GLAD_GL_KHR_texture_compression_astc_ldr + gl.GLAD_GL_KHR_texture_compression_astc_ldr or + defined(ios) or + defined(myouForceAstc) +const myouMinTextureChannels {.intdefine.} = 0 const myouEngineNumTextureThreads {.intdefine.} = 4 const myouBC7VerboseMode {.booldefine.} = false +const myouAllCacheFilesExist {.booldefine.} = defined(myouUseAndroidAssets) +const myouLoadUncompressedTextures {.booldefine.} = false +when defined(myouAllCacheFilesExist): + import ../platform/platform + import std/strutils template u32(x: untyped): uint32 = cast[uint32](x) @@ -90,7 +98,7 @@ template block_file_size(width, height, depth, block_x, block_y, block_z, block_ let blocks_z = (depth.int + block_z.int - 1) div block_z.int blocks_x * blocks_y * blocks_z * block_byte_size.int -proc make_mipmaps(tex: Texture, pixels: SliceMem[byte], compress: CompressMipmap): (seq[KtxPart], seq[SliceMem[byte]]) = +proc make_mipmaps(tex: Texture, pixels: SliceMem[byte], compress: CompressMipmap, fmt_debug: string): (seq[KtxPart], seq[SliceMem[byte]]) = var width = tex.width.int32 var height = tex.height.int32 let depth = tex.format_depth @@ -147,7 +155,7 @@ proc make_mipmaps(tex: Texture, pixels: SliceMem[byte], compress: CompressMipmap h = max(1, h shr 1) mip_level.inc let time2 = getmonotime().ticks.float/1000000000 - echo "time: ", time2-time, " ", tex.name + echo "time: ", time2-time, " ", tex.name, " ", fmt_debug return (parts, data_refs) when defined(myouUseBC7Encoder): @@ -183,7 +191,7 @@ when defined(myouUseBC7Encoder): assert err == NO_ERROR, "bc7enc error: " & $err return (data, row_len) - let (parts, data_refs) = make_mipmaps(tex, pixels, compress) + let (parts, data_refs) = make_mipmaps(tex, pixels, compress, "bc") let info = KtxInfo( width: parts[0].width, height: parts[0].height, depth: tex.format_depth.int32, @@ -256,7 +264,7 @@ when defined(myouUseAstcEncoder): assert err == ASTCENC_SUCCESS, "ASTC encoding error: " & $err return (data, row_len) - let (parts, data_refs) = make_mipmaps(tex, pixels, compress) + let (parts, data_refs) = make_mipmaps(tex, pixels, compress, "astc") let blk = (blk_size.first, blk_size.second) let info = KtxInfo( @@ -274,12 +282,12 @@ func `%`(p: pointer): JsonNode = %0 proc loadOptimized*(tex: Texture, slices: seq[SliceMem[byte]], callback_uncompressed: CallbackUncompressed = nil, callback_compressed: CallbackCompressed = nil, - flip = true, min_channels = 0) {.gcsafe.} = + flip = true, min_channels = myouMinTextureChannels) {.gcsafe.} = let settings = tex.engine.cache_settings var min_channels = min_channels var will_compress = settings.compress_textures - var will_load_uncompressed_first = false + var will_load_uncompressed_first = myouLoadUncompressedTextures var will_encode_all = settings.compress_all_formats will_compress = will_compress and @@ -302,16 +310,28 @@ proc loadOptimized*(tex: Texture, slices: seq[SliceMem[byte]], if settings.use_cache and callback_compressed != nil: let cache_file = settings.cache_dir & "/" & cache_file_name # TODO: allow networked requests - if fileExists cache_file: + if myouAllCacheFilesExist or fileExists cache_file: try: - let slices = readFile(cache_file).decompress.toSliceMem.to(byte).deserialize + when defined(myouUseAndroidAssets): + var asset = myouAndroidAPKFilePointerLength(cache_file.split("/", 1)[1]) + var f = newString(asset.len) + copyMem(f.cstring.pointer, asset.p, asset.len) + let slices = f.decompress.toSliceMem.to(byte).deserialize + elif defined(ios) and myouAllCacheFilesExist: + let cache_file = myouGetBundledFilePath("assets/" & cache_file.split("/", 1)[1]) + let slices = readFile(cache_file).decompress.toSliceMem.to(byte).deserialize + else: + let slices = readFile(cache_file).decompress.toSliceMem.to(byte).deserialize var data = to[KtxInfoParts](slices[^1].toString) for i,p in data.parts.mpairs: p.data = slices[i].toPointer tex.callback_compressed(data, slices) return - except: + except Exception as e: + echo e.getStackTrace() + echo getCurrentExceptionMsg() # TODO: proper error handling and logging + echo cache_file echo "ERROR: could not load cache file for " & tex.name if not (native_bc or native_astc): @@ -344,7 +364,7 @@ proc loadOptimized*(tex: Texture, slices: seq[SliceMem[byte]], let channels = tex.format.channel_count - when defined(myouUseBC7Encoder): + when defined(myouUseBC7Encoder) and not defined(myouForceAstc): if has_bptc_support or will_encode_all: let bc_format = if channels == 1: 4.int8 # BC4 @@ -391,7 +411,7 @@ when compileOption("threads"): proc loadOptimizedThreaded*(tex: Texture, slices: seq[SliceMem[byte]], callback_uncompressed: CallbackUncompressed = nil, callback_compressed: CallbackCompressed = nil, - flip = true, min_channels = 0) = + flip = true, min_channels = myouMinTextureChannels) = when not compileOption("threads"): loadOptimized(tex, slices, callback_uncompressed, callback_compressed, flip, min_channels) diff --git a/src/graphics/texture.nim b/src/graphics/texture.nim index ac001ab..c22a18f 100644 --- a/src/graphics/texture.nim +++ b/src/graphics/texture.nim @@ -59,11 +59,14 @@ proc newTexture*(engine: MyouEngine, name: string, width, height: int, depth: in pixels: ArrRef[float32] = nil): Texture proc generateMipmap*(self: Texture) func to_sRGB*(format: TextureFormat): TextureFormat -proc newTexture*(engine: MyouEngine, name: string, data: SliceMem[byte], is_sRGB: bool, filter: TextureFilter = Trilinear, depth=1, flip=true): Texture +proc newTexture*(engine: MyouEngine, name: string, data: SliceMem[byte], + is_sRGB: bool, filter: TextureFilter = Trilinear, depth=1, + flip = true, use_compression = true): Texture proc newTexture*(engine: MyouEngine, name: string, file_name: string, is_sRGB: bool, filter: TextureFilter = Trilinear, tex_type: TextureType = Tex2D, flip = true, + use_compression = true, ): Texture proc setExtrapolation*(self: Texture, ext: TextureExtrapolation) proc getTexturePixels*(self: Texture): TexturePixels @@ -440,7 +443,9 @@ func to_sRGB*(format: TextureFormat): TextureFormat = of RGB_u8: SRGB_u8 else: raise newException(ValueError, "There's no sRGB version of " & $format) -proc newTexture*(engine: MyouEngine, name: string, data: SliceMem[byte], is_sRGB: bool, filter: TextureFilter = Trilinear, depth=1, flip=true): Texture = +proc newTexture*(engine: MyouEngine, name: string, data: SliceMem[byte], + is_sRGB: bool, filter: TextureFilter = Trilinear, depth=1, + flip = true, use_compression = true): Texture = var (width, height, format) = getDimensionsFormat(data.data, data.byte_len) if is_sRGB: if format in [RGB_u8, RGBA_u8]: @@ -451,13 +456,17 @@ proc newTexture*(engine: MyouEngine, name: string, data: SliceMem[byte], is_sRGB let self = engine.newTexture(name, width, height, depth, format, filter=filter) self.is_sRGB = is_sRGB engine.renderer.enqueue proc() = - self.loadOptimizedThreaded(@[data], loadFromPixels, loadCompressedData) + if use_compression: + self.loadOptimizedThreaded(@[data], loadFromPixels, loadCompressedData) + else: + self.loadOptimizedThreaded(@[data], loadFromPixels, nil) return self proc newTexture*(engine: MyouEngine, name: string, file_name: string, is_sRGB: bool, filter: TextureFilter = Trilinear, tex_type: TextureType = Tex2D, flip = true, + use_compression = true, ): Texture = # TODO: Api that stores the LoadableResource so it can be re-loaded later @@ -506,7 +515,10 @@ proc newTexture*(engine: MyouEngine, name: string, file_name: string, is_sRGB: b engine.renderer.enqueue proc()= try: self.ensure_storage() - self.loadOptimizedThreaded(@[data], loadFromPixels, loadCompressedData) + if use_compression: + self.loadOptimizedThreaded(@[data], loadFromPixels, loadCompressedData) + else: + self.loadOptimizedThreaded(@[data], loadFromPixels, nil) except: # TODO: use logging echo getCurrentExceptionMsg() diff --git a/src/platform/glfw_wrap.nim b/src/platform/glfw_wrap.nim index f62a99f..9de3385 100644 --- a/src/platform/glfw_wrap.nim +++ b/src/platform/glfw_wrap.nim @@ -50,6 +50,8 @@ when compileOption("threads"): from loadable import terminateLoadableWorkerThreads from ../gpu_formats/texture_optimize import terminateTextureWorkerThreads +func c_strstr(haystack, needle: cstring): cstring {.importc: "strstr", header: "".} + proc screen*(window: Window): Screen {.inline.} = cast[Screen](window.getWindowUserPointer) @@ -211,6 +213,10 @@ proc init_graphics*(engine: MyouEngine, width, height: int32, title: string, if max_messages == 0: return # dump (source, etype, id, severity, length) + when defined(myouForceAstc): + # Supress warnings when forcing astc on desktop + if c_strstr(message, "emulating compressed format") != nil: + return echo getStackTrace().rsplit('\n',3)[1] echo "OpenGL error: ", message max_messages -= 1