diff --git a/source/graphics/MiniMapTexture.cpp b/source/graphics/MiniMapTexture.cpp index 01dc3727b0..4c86e8673f 100644 --- a/source/graphics/MiniMapTexture.cpp +++ b/source/graphics/MiniMapTexture.cpp @@ -28,6 +28,7 @@ #include "graphics/TerrainTextureEntry.h" #include "graphics/TerrainTextureManager.h" #include "graphics/TerritoryTexture.h" +#include "graphics/TextureManager.h" #include "lib/bits.h" #include "lib/timer.h" #include "ps/ConfigDB.h" diff --git a/source/graphics/TerrainTextureEntry.cpp b/source/graphics/TerrainTextureEntry.cpp index 03a81eae11..c8a435015a 100644 --- a/source/graphics/TerrainTextureEntry.cpp +++ b/source/graphics/TerrainTextureEntry.cpp @@ -23,11 +23,8 @@ #include "graphics/Terrain.h" #include "graphics/TerrainProperties.h" #include "graphics/TerrainTextureManager.h" -#include "graphics/Texture.h" -#include "lib/allocators/shared_ptr.h" -#include "lib/file/io/io.h" +#include "graphics/TextureManager.h" #include "lib/ogl.h" -#include "lib/tex/tex.h" #include "lib/utf8.h" #include "ps/CLogger.h" #include "ps/CStrInternStatic.h" @@ -143,7 +140,7 @@ CTerrainTextureEntry::CTerrainTextureEntry(CTerrainPropertiesPtr properties, con } if (CRenderer::IsInitialised()) - LoadAlphaMaps(alphamap); + m_TerrainAlpha = g_TexMan.LoadAlphaMap(alphamap); float texAngle = 0.f; float texSize = 1.f; @@ -197,172 +194,3 @@ const float* CTerrainTextureEntry::GetTextureMatrix() const { return &m_TextureMatrix._11; } - -// LoadAlphaMaps: load the 14 default alpha maps, pack them into one composite texture and -// calculate the coordinate of each alphamap within this packed texture -void CTerrainTextureEntry::LoadAlphaMaps(const VfsPath& alphaMapType) -{ - const std::wstring key = L"(alpha map composite" + alphaMapType.string() + L")"; - - CTerrainTextureManager::TerrainAlphaMap::iterator it = g_TexMan.m_TerrainAlphas.find(alphaMapType); - - if (it != g_TexMan.m_TerrainAlphas.end()) - { - m_TerrainAlpha = it; - return; - } - - g_TexMan.m_TerrainAlphas[alphaMapType] = TerrainAlpha(); - it = g_TexMan.m_TerrainAlphas.find(alphaMapType); - - TerrainAlpha &result = it->second; - - // - // load all textures and store Handle in array - // - Tex textures[NUM_ALPHA_MAPS] = {}; - const VfsPath path = VfsPath("art/textures/terrain/alphamaps") / alphaMapType; - - const wchar_t* fnames[NUM_ALPHA_MAPS] = - { - L"blendcircle.png", - L"blendlshape.png", - L"blendedge.png", - L"blendedgecorner.png", - L"blendedgetwocorners.png", - L"blendfourcorners.png", - L"blendtwooppositecorners.png", - L"blendlshapecorner.png", - L"blendtwocorners.png", - L"blendcorner.png", - L"blendtwoedges.png", - L"blendthreecorners.png", - L"blendushape.png", - L"blendbad.png" - }; - size_t base = 0; // texture width/height (see below) - // for convenience, we require all alpha maps to be of the same BPP - // (avoids another ogl_tex_get_size call, and doesn't hurt) - size_t bpp = 0; - for (size_t i = 0; i < NUM_ALPHA_MAPS; ++i) - { - // note: these individual textures can be discarded afterwards; - // we cache the composite. - std::shared_ptr fileData; - size_t fileSize; - if (g_VFS->LoadFile(path / fnames[i], fileData, fileSize) != INFO::OK || - textures[i].decode(fileData, fileSize) != INFO::OK) - { - g_TexMan.m_TerrainAlphas.erase(it); - LOGERROR("Failed to load alphamap: %s", alphaMapType.string8()); - - const VfsPath standard("standard"); - if (path != standard) - LoadAlphaMaps(standard); - return; - } - - // Get its size and make sure they are all equal. - // (the packing algo assumes this). - if (textures[i].m_Width != textures[i].m_Height) - DEBUG_DISPLAY_ERROR(L"Alpha maps are not square"); - // .. first iteration: establish size - if (i == 0) - { - base = textures[i].m_Width; - bpp = textures[i].m_Bpp; - } - // .. not first: make sure texture size matches - else if (base != textures[i].m_Width || bpp != textures[i].m_Bpp) - DEBUG_DISPLAY_ERROR(L"Alpha maps are not identically sized (including pixel depth)"); - } - - // - // copy each alpha map (tile) into one buffer, arrayed horizontally. - // - const size_t tileWidth = 2 + base + 2; // 2 pixel border (avoids bilinear filtering artifacts) - const size_t totalWidth = round_up_to_pow2(tileWidth * NUM_ALPHA_MAPS); - const size_t totalHeight = base; ENSURE(is_pow2(totalHeight)); - std::shared_ptr data; - AllocateAligned(data, totalWidth * totalHeight, maxSectorSize); - // for each tile on row - for (size_t i = 0; i < NUM_ALPHA_MAPS; ++i) - { - // get src of copy - u8* src = textures[i].get_data(); - ENSURE(src); - - const size_t srcStep = bpp / 8; - - // get destination of copy - u8* dst = data.get() + (i * tileWidth); - - // for each row of image - for (size_t j = 0; j < base; ++j) - { - // duplicate first pixel - *dst++ = *src; - *dst++ = *src; - - // copy a row - for (size_t k = 0; k < base; ++k) - { - *dst++ = *src; - src += srcStep; - } - - // duplicate last pixel - *dst++ = *(src - srcStep); - *dst++ = *(src - srcStep); - - // advance write pointer for next row - dst += totalWidth - tileWidth; - } - - result.m_AlphaMapCoords[i].u0 = static_cast(i * tileWidth + 2) / totalWidth; - result.m_AlphaMapCoords[i].u1 = static_cast((i + 1) * tileWidth - 2) / totalWidth; - result.m_AlphaMapCoords[i].v0 = 0.0f; - result.m_AlphaMapCoords[i].v1 = 1.0f; - } - - for (size_t i = 0; i < NUM_ALPHA_MAPS; ++i) - textures[i].free(); - - // Enable the following to save a png of the generated texture - // in the public/ directory, for debugging. -#if 0 - Tex t; - ignore_result(t.wrap(totalWidth, totalHeight, 8, TEX_GREY, data, 0)); - - const VfsPath filename("blendtex.png"); - - DynArray da; - RETURN_STATUS_IF_ERR(tex_encode(&t, filename.Extension(), &da)); - - // write to disk - //Status ret = INFO::OK; - { - std::shared_ptr file = DummySharedPtr(da.base); - const ssize_t bytes_written = g_VFS->CreateFile(filename, file, da.pos); - if(bytes_written > 0) - ENSURE(bytes_written == (ssize_t)da.pos); - //else - // ret = (Status)bytes_written; - } - - ignore_result(da_free(&da)); -#endif - - result.m_CompositeAlphaMap = Renderer::Backend::GL::CTexture::Create2D( - Renderer::Backend::Format::A8, totalWidth, totalHeight, - Renderer::Backend::Sampler::MakeDefaultSampler( - Renderer::Backend::Sampler::Filter::LINEAR, - Renderer::Backend::Sampler::AddressMode::CLAMP_TO_EDGE)); - - // Upload the composite texture. - g_Renderer.BindTexture(0, result.m_CompositeAlphaMap->GetHandle()); - glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, totalWidth, totalHeight, 0, GL_ALPHA, GL_UNSIGNED_BYTE, data.get()); - g_Renderer.BindTexture(0, 0); - - m_TerrainAlpha = it; -} diff --git a/source/graphics/TerrainTextureEntry.h b/source/graphics/TerrainTextureEntry.h index 2b74204569..b6722dfba7 100644 --- a/source/graphics/TerrainTextureEntry.h +++ b/source/graphics/TerrainTextureEntry.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 Wildfire Games. +/* Copyright (C) 2022 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -20,9 +20,8 @@ #include "graphics/Material.h" #include "graphics/TerrainTextureManager.h" -#include "graphics/TextureManager.h" +#include "graphics/Texture.h" #include "lib/file/vfs/vfs_path.h" -#include "lib/res/handle.h" #include "maths/Matrix3D.h" #include "ps/CStr.h" @@ -91,8 +90,6 @@ private: // calculate the root color of the texture, used for coloring minimap void BuildBaseColor(); - - void LoadAlphaMaps(const VfsPath& alphaMapType); }; #endif diff --git a/source/graphics/TerrainTextureManager.cpp b/source/graphics/TerrainTextureManager.cpp index 945eac63e9..08b40ac5d2 100644 --- a/source/graphics/TerrainTextureManager.cpp +++ b/source/graphics/TerrainTextureManager.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 Wildfire Games. +/* Copyright (C) 2022 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -21,10 +21,16 @@ #include "graphics/TerrainTextureEntry.h" #include "graphics/TerrainProperties.h" +#include "graphics/TextureManager.h" +#include "lib/allocators/shared_ptr.h" +#include "lib/bits.h" +#include "lib/ogl.h" +#include "lib/tex/tex.h" #include "lib/timer.h" #include "ps/CLogger.h" #include "ps/Filesystem.h" #include "ps/XML/Xeromyces.h" +#include "renderer/Renderer.h" #include #include @@ -133,6 +139,189 @@ CTerrainGroup* CTerrainTextureManager::FindGroup(const CStr& name) return m_TerrainGroups[name] = new CTerrainGroup(name, ++m_LastGroupIndex); } +// LoadAlphaMaps: load the 14 default alpha maps, pack them into one composite texture and +// calculate the coordinate of each alphamap within this packed texture. +CTerrainTextureManager::TerrainAlphaMap::iterator +CTerrainTextureManager::LoadAlphaMap(const VfsPath& alphaMapType) +{ + const std::wstring key = L"(alpha map composite" + alphaMapType.string() + L")"; + + CTerrainTextureManager::TerrainAlphaMap::iterator it = m_TerrainAlphas.find(alphaMapType); + + if (it != g_TexMan.m_TerrainAlphas.end()) + return it; + + m_TerrainAlphas[alphaMapType] = TerrainAlpha(); + it = m_TerrainAlphas.find(alphaMapType); + + TerrainAlpha& result = it->second; + + // + // load all textures and store Handle in array + // + Tex textures[NUM_ALPHA_MAPS] = {}; + const VfsPath path = VfsPath("art/textures/terrain/alphamaps") / alphaMapType; + + const wchar_t* fnames[NUM_ALPHA_MAPS] = + { + L"blendcircle.png", + L"blendlshape.png", + L"blendedge.png", + L"blendedgecorner.png", + L"blendedgetwocorners.png", + L"blendfourcorners.png", + L"blendtwooppositecorners.png", + L"blendlshapecorner.png", + L"blendtwocorners.png", + L"blendcorner.png", + L"blendtwoedges.png", + L"blendthreecorners.png", + L"blendushape.png", + L"blendbad.png" + }; + size_t base = 0; // texture width/height (see below) + // For convenience, we require all alpha maps to be of the same BPP. + size_t bpp = 0; + for (size_t i = 0; i < NUM_ALPHA_MAPS; ++i) + { + // note: these individual textures can be discarded afterwards; + // we cache the composite. + std::shared_ptr fileData; + size_t fileSize; + if (g_VFS->LoadFile(path / fnames[i], fileData, fileSize) != INFO::OK || + textures[i].decode(fileData, fileSize) != INFO::OK) + { + m_TerrainAlphas.erase(it); + LOGERROR("Failed to load alphamap: %s", alphaMapType.string8()); + + const VfsPath standard("standard"); + if (path != standard) + return LoadAlphaMap(standard); + return m_TerrainAlphas.end(); + } + + // Get its size and make sure they are all equal. + // (the packing algo assumes this). + if (textures[i].m_Width != textures[i].m_Height) + DEBUG_DISPLAY_ERROR(L"Alpha maps are not square"); + // .. first iteration: establish size + if (i == 0) + { + base = textures[i].m_Width; + bpp = textures[i].m_Bpp; + } + // .. not first: make sure texture size matches + else if (base != textures[i].m_Width || bpp != textures[i].m_Bpp) + DEBUG_DISPLAY_ERROR(L"Alpha maps are not identically sized (including pixel depth)"); + } + + // + // copy each alpha map (tile) into one buffer, arrayed horizontally. + // + const size_t tileWidth = 2 + base + 2; // 2 pixel border (avoids bilinear filtering artifacts) + const size_t totalWidth = round_up_to_pow2(tileWidth * NUM_ALPHA_MAPS); + const size_t totalHeight = base; ENSURE(is_pow2(totalHeight)); + std::shared_ptr data; + AllocateAligned(data, totalWidth * totalHeight, maxSectorSize); + // for each tile on row + for (size_t i = 0; i < NUM_ALPHA_MAPS; ++i) + { + // get src of copy + u8* src = textures[i].get_data(); + ENSURE(src); + + const size_t srcStep = bpp / 8; + + // get destination of copy + u8* dst = data.get() + (i * tileWidth); + + // for each row of image + for (size_t j = 0; j < base; ++j) + { + // duplicate first pixel + *dst++ = *src; + *dst++ = *src; + + // copy a row + for (size_t k = 0; k < base; ++k) + { + *dst++ = *src; + src += srcStep; + } + + // duplicate last pixel + *dst++ = *(src - srcStep); + *dst++ = *(src - srcStep); + + // advance write pointer for next row + dst += totalWidth - tileWidth; + } + + result.m_AlphaMapCoords[i].u0 = static_cast(i * tileWidth + 2) / totalWidth; + result.m_AlphaMapCoords[i].u1 = static_cast((i + 1) * tileWidth - 2) / totalWidth; + result.m_AlphaMapCoords[i].v0 = 0.0f; + result.m_AlphaMapCoords[i].v1 = 1.0f; + } + + for (size_t i = 0; i < NUM_ALPHA_MAPS; ++i) + textures[i].free(); + + // Enable the following to save a png of the generated texture + // in the public/ directory, for debugging. +#if 0 + Tex t; + ignore_result(t.wrap(totalWidth, totalHeight, 8, TEX_GREY, data, 0)); + + const VfsPath filename("blendtex.png"); + + DynArray da; + RETURN_STATUS_IF_ERR(tex_encode(&t, filename.Extension(), &da)); + + // write to disk + //Status ret = INFO::OK; + { + std::shared_ptr file = DummySharedPtr(da.base); + const ssize_t bytes_written = g_VFS->CreateFile(filename, file, da.pos); + if (bytes_written > 0) + ENSURE(bytes_written == (ssize_t)da.pos); + //else + // ret = (Status)bytes_written; + } + + ignore_result(da_free(&da)); +#endif + + result.m_CompositeAlphaMap = Renderer::Backend::GL::CTexture::Create2D( + Renderer::Backend::Format::A8, totalWidth, totalHeight, + Renderer::Backend::Sampler::MakeDefaultSampler( + Renderer::Backend::Sampler::Filter::LINEAR, + Renderer::Backend::Sampler::AddressMode::CLAMP_TO_EDGE)); + + result.m_CompositeDataToUpload = std::move(data); + + m_AlphaMapsToUpload.emplace_back(it); + + return it; +} + +void CTerrainTextureManager::UploadResourcesIfNeeded( + Renderer::Backend::GL::CDeviceCommandContext* deviceCommandContext) +{ + for (const CTerrainTextureManager::TerrainAlphaMap::iterator& it : m_AlphaMapsToUpload) + { + TerrainAlpha& alphaMap = it->second; + if (!alphaMap.m_CompositeDataToUpload) + continue; + // Upload the composite texture. + deviceCommandContext->UploadTexture(alphaMap.m_CompositeAlphaMap.get(), + Renderer::Backend::Format::A8, alphaMap.m_CompositeDataToUpload.get(), + alphaMap.m_CompositeAlphaMap->GetWidth() * alphaMap.m_CompositeAlphaMap->GetHeight()); + alphaMap.m_CompositeDataToUpload.reset(); + } + + m_AlphaMapsToUpload.clear(); +} + void CTerrainGroup::AddTerrain(CTerrainTextureEntry* pTerrain) { m_Terrains.push_back(pTerrain); diff --git a/source/graphics/TerrainTextureManager.h b/source/graphics/TerrainTextureManager.h index 0406308555..c9c7699e28 100644 --- a/source/graphics/TerrainTextureManager.h +++ b/source/graphics/TerrainTextureManager.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 Wildfire Games. +/* Copyright (C) 2022 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -22,6 +22,7 @@ #include "lib/res/handle.h" #include "ps/CStr.h" #include "ps/Singleton.h" +#include "renderer/backend/gl/DeviceCommandContext.h" #include "renderer/backend/gl/Texture.h" #include @@ -73,6 +74,8 @@ struct TerrainAlpha { // Composite alpha map (all the alpha maps packed into one texture). std::unique_ptr m_CompositeAlphaMap; + // Data is used to separate file loading and uploading to GPU. + std::shared_ptr m_CompositeDataToUpload; // Coordinates of each (untransformed) alpha map within the packed texture. struct { @@ -88,21 +91,9 @@ class CTerrainTextureManager : public Singleton friend class CTerrainTextureEntry; public: - typedef std::map TerrainGroupMap; - typedef std::map TerrainAlphaMap; + using TerrainGroupMap = std::map; + using TerrainAlphaMap = std::map; -private: - // All texture entries created by this class, for easy freeing now that - // textures may be in several STextureType's - std::vector m_TextureEntries; - - TerrainGroupMap m_TerrainGroups; - - TerrainAlphaMap m_TerrainAlphas; - - size_t m_LastGroupIndex; - -public: // constructor, destructor CTerrainTextureManager(); ~CTerrainTextureManager(); @@ -128,7 +119,25 @@ public: const TerrainGroupMap& GetGroups() const { return m_TerrainGroups; } + + CTerrainTextureManager::TerrainAlphaMap::iterator LoadAlphaMap(const VfsPath& alphaMapType); + + void UploadResourcesIfNeeded(Renderer::Backend::GL::CDeviceCommandContext* deviceCommandContext); + +private: + // All texture entries created by this class, for easy freeing now that + // textures may be in several STextureType's + std::vector m_TextureEntries; + + TerrainGroupMap m_TerrainGroups; + + TerrainAlphaMap m_TerrainAlphas; + + size_t m_LastGroupIndex; + + // A way to separate file loading and uploading to GPU to not stall uploading. + // Once we get a properly threaded loading we might optimize that. + std::vector m_AlphaMapsToUpload; }; - -#endif +#endif // INCLUDED_TERRAINTEXTUREMANAGER diff --git a/source/gui/ObjectTypes/CMiniMap.cpp b/source/gui/ObjectTypes/CMiniMap.cpp index c69b082a3c..f2ed875ad8 100644 --- a/source/gui/ObjectTypes/CMiniMap.cpp +++ b/source/gui/ObjectTypes/CMiniMap.cpp @@ -29,6 +29,7 @@ #include "graphics/Terrain.h" #include "graphics/TerrainTextureEntry.h" #include "graphics/TerrainTextureManager.h" +#include "graphics/TextureManager.h" #include "gui/CGUI.h" #include "gui/GUIManager.h" #include "gui/GUIMatrix.h" diff --git a/source/renderer/PatchRData.cpp b/source/renderer/PatchRData.cpp index 836af4b8c0..0ea12da910 100644 --- a/source/renderer/PatchRData.cpp +++ b/source/renderer/PatchRData.cpp @@ -27,6 +27,7 @@ #include "graphics/Terrain.h" #include "graphics/TerrainTextureEntry.h" #include "graphics/TextRenderer.h" +#include "graphics/TextureManager.h" #include "lib/allocators/DynamicArena.h" #include "lib/allocators/STLAllocators.h" #include "maths/MathUtil.h" diff --git a/source/renderer/Renderer.cpp b/source/renderer/Renderer.cpp index eba8a55e1c..0dfde82e6e 100644 --- a/source/renderer/Renderer.cpp +++ b/source/renderer/Renderer.cpp @@ -494,6 +494,8 @@ void CRenderer::RenderFrameImpl(bool renderGUI, bool renderLogger) g_Profiler2.RecordGPUFrameStart(); ogl_WarnIfError(); + g_TexMan.UploadResourcesIfNeeded(m->deviceCommandContext.get()); + // prepare before starting the renderer frame if (g_Game && g_Game->IsGameStarted()) g_Game->GetView()->BeginFrame(); diff --git a/source/tools/atlas/GameInterface/Handlers/TerrainHandlers.cpp b/source/tools/atlas/GameInterface/Handlers/TerrainHandlers.cpp index 15c6589e31..dad93208cc 100644 --- a/source/tools/atlas/GameInterface/Handlers/TerrainHandlers.cpp +++ b/source/tools/atlas/GameInterface/Handlers/TerrainHandlers.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 Wildfire Games. +/* Copyright (C) 2022 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -25,6 +25,7 @@ #include "graphics/TerrainTextureManager.h" #include "graphics/TerrainTextureEntry.h" #include "graphics/Terrain.h" +#include "graphics/TextureManager.h" #include "ps/Game.h" #include "ps/World.h" #include "lib/tex/tex.h"