Index: binaries/data/config/default.cfg =================================================================== --- binaries/data/config/default.cfg +++ binaries/data/config/default.cfg @@ -122,6 +122,12 @@ ; Should not be edited. It's used only for preventing of running fixed pipeline. renderpath = default +; (0 - low, 1 - medium, 2 - high), higher quality means worse performance. +textures.quality = 2 + +; (1, 2, 4, 8 and 16) +textures.maxanisotropy = 2 + ;;;;; EXPERIMENTAL ;;;;; ; Experimental probably-non-working GPU skinning support; requires GLSL; use at own risk gpuskinning = false Index: binaries/data/mods/public/gui/options/options.json =================================================================== --- binaries/data/mods/public/gui/options/options.json +++ binaries/data/mods/public/gui/options/options.json @@ -343,6 +343,30 @@ "tooltip": "Use actual water depth in rendering calculations.", "dependencies": ["watereffects", "waterrefraction"], "config": "waterrealdepth" + }, + { + "type": "dropdown", + "label": "Textures quality", + "tooltip": "Decrease texture quality making them blurrier but increases game performance.", + "config": "textures.quality", + "list": [ + { "value": 0, "label": "Low", "tooltip": "Low" }, + { "value": 1, "label": "Medium", "tooltip": "Medium" }, + { "value": 2, "label": "High", "tooltip": "High" } + ] + }, + { + "type": "dropdown", + "label": "Textures anisotropic filter", + "tooltip": "Makes textures look better, especially terrain. If the anisotropic filter value is unsupported it will be set to the max supported value.", + "config": "textures.maxanisotropy", + "list": [ + { "value": 1, "label": "1x", "tooltip": "1x" }, + { "value": 2, "label": "2x", "tooltip": "2x" }, + { "value": 4, "label": "4x", "tooltip": "4x" }, + { "value": 8, "label": "8x", "tooltip": "8x" }, + { "value": 16, "label": "16x", "tooltip": "16x" } + ] } ] }, Index: source/graphics/Canvas2D.cpp =================================================================== --- source/graphics/Canvas2D.cpp +++ source/graphics/Canvas2D.cpp @@ -38,11 +38,14 @@ // Array of 2D elements unrolled into 1D array. using PlaneArray2D = std::array; -inline void DrawTextureImpl(const CShaderProgramPtr& shader, CTexturePtr texture, +inline void DrawTextureImpl( + Renderer::Backend::GL::CDeviceCommandContext* deviceCommandContext, + const CShaderProgramPtr& shader, const CTexturePtr& texture, const PlaneArray2D& vertices, PlaneArray2D uvs, const CColor& multiply, const CColor& add, const float grayscaleFactor) { - shader->BindTexture(str_tex, texture); + texture->UploadBackendTextureIfNeeded(deviceCommandContext); + shader->BindTexture(str_tex, texture->GetBackendTexture()); for (size_t idx = 0; idx < uvs.size(); idx += 2) { if (texture->GetWidth() > 0.0f) @@ -67,6 +70,12 @@ class CCanvas2D::Impl { public: + Impl() + // TODO: remove global renderer access as pass as an argument. + : DeviceCommandContext(g_Renderer.GetDeviceCommandContext()) + { + } + void BindTechIfNeeded() { if (Tech) @@ -76,8 +85,7 @@ Tech = g_Renderer.GetShaderManager().LoadEffect(str_canvas2d, defines); ENSURE(Tech); Tech->BeginPass(); - // TODO: remove global renderer access. - g_Renderer.GetDeviceCommandContext()->SetGraphicsPipelineState( + DeviceCommandContext->SetGraphicsPipelineState( Tech->GetGraphicsPipelineStateDesc()); } @@ -90,6 +98,7 @@ Tech.reset(); } + Renderer::Backend::GL::CDeviceCommandContext* DeviceCommandContext; CShaderTechniquePtr Tech; }; @@ -117,7 +126,7 @@ } CShaderProgramPtr shader = m->Tech->GetShader(); - shader->BindTexture(str_tex, g_Renderer.GetTextureManager().GetTransparentTexture()); + shader->BindTexture(str_tex, g_Renderer.GetTextureManager().GetTransparentTexture()->GetBackendTexture()); shader->Uniform(str_transform, GetDefaultGuiMatrix()); shader->Uniform(str_colorAdd, color); shader->Uniform(str_colorMul, CColor(0.0f, 0.0f, 0.0f, 0.0f)); @@ -151,7 +160,8 @@ m->BindTechIfNeeded(); DrawTextureImpl( - m->Tech->GetShader(), g_Renderer.GetTextureManager().GetTransparentTexture(), + m->DeviceCommandContext, m->Tech->GetShader(), + g_Renderer.GetTextureManager().GetTransparentTexture(), vertices, uvs, CColor(0.0f, 0.0f, 0.0f, 0.0f), color, 0.0f); } @@ -180,7 +190,8 @@ }; m->BindTechIfNeeded(); - DrawTextureImpl(m->Tech->GetShader(), texture, vertices, uvs, multiply, add, grayscaleFactor); + DrawTextureImpl(m->DeviceCommandContext, m->Tech->GetShader(), + texture, vertices, uvs, multiply, add, grayscaleFactor); } void CCanvas2D::DrawText(CTextRenderer& textRenderer) @@ -190,7 +201,7 @@ CShaderProgramPtr shader = m->Tech->GetShader(); shader->Uniform(str_grayscaleFactor, 0.0f); - textRenderer.Render(shader, GetDefaultGuiMatrix()); + textRenderer.Render(m->DeviceCommandContext, shader, GetDefaultGuiMatrix()); } void CCanvas2D::Flush() Index: source/graphics/FontManager.cpp =================================================================== --- source/graphics/FontManager.cpp +++ source/graphics/FontManager.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 @@ -139,10 +139,9 @@ // Load glyph texture const VfsPath imageName(fontName.string() + ".png"); - CTextureProperties textureProps(path / imageName); - textureProps.SetFilter(GL_LINEAR); - if (!font->m_HasRGB) - textureProps.SetFormatOverride(GL_ALPHA); + CTextureProperties textureProps(path / imageName, + font->m_HasRGB ? Renderer::Backend::Format::R8G8B8A8 : Renderer::Backend::Format::A8); + textureProps.SetIgnoreQuality(true); font->m_Texture = g_Renderer.GetTextureManager().CreateTexture(textureProps); return true; Index: source/graphics/LOSTexture.cpp =================================================================== --- source/graphics/LOSTexture.cpp +++ source/graphics/LOSTexture.cpp @@ -229,7 +229,7 @@ Renderer::Backend::Sampler::Filter::LINEAR, Renderer::Backend::Sampler::AddressMode::CLAMP_TO_EDGE); - m_Texture = Renderer::Backend::GL::CTexture::Create2D( + m_Texture = deviceCommandContext->GetDevice()->CreateTexture2D("LOSTexture", Renderer::Backend::Format::A8, textureSize, textureSize, defaultSamplerDesc); // Initialise texture with SoD color, for the areas we don't @@ -239,9 +239,9 @@ if (CRenderer::IsInitialised() && g_RenderingOptions.GetSmoothLOS()) { - m_SmoothTextures[0] = Renderer::Backend::GL::CTexture::Create2D( + m_SmoothTextures[0] = deviceCommandContext->GetDevice()->CreateTexture2D("LOSSmoothTexture0", Renderer::Backend::Format::A8, textureSize, textureSize, defaultSamplerDesc); - m_SmoothTextures[1] = Renderer::Backend::GL::CTexture::Create2D( + m_SmoothTextures[1] = deviceCommandContext->GetDevice()->CreateTexture2D("LOSSmoothTexture1", Renderer::Backend::Format::A8, textureSize, textureSize, defaultSamplerDesc); m_SmoothFramebuffers[0] = Renderer::Backend::GL::CFramebuffer::Create( @@ -254,11 +254,17 @@ g_RenderingOptions.SetSmoothLOS(false); } - deviceCommandContext->UploadTexture(m_SmoothTextures[0].get(), Renderer::Backend::Format::A8, texData.get(), textureSize * textureSize); - deviceCommandContext->UploadTexture(m_SmoothTextures[1].get(), Renderer::Backend::Format::A8, texData.get(), textureSize * textureSize); + deviceCommandContext->UploadTexture( + m_SmoothTextures[0].get(), Renderer::Backend::Format::A8, + texData.get(), textureSize * textureSize); + deviceCommandContext->UploadTexture( + m_SmoothTextures[1].get(), Renderer::Backend::Format::A8, + texData.get(), textureSize * textureSize); } - deviceCommandContext->UploadTexture(m_Texture.get(), Renderer::Backend::Format::A8, texData.get(), textureSize * textureSize); + deviceCommandContext->UploadTexture( + m_Texture.get(), Renderer::Backend::Format::A8, + texData.get(), textureSize * textureSize); texData.reset(); Index: source/graphics/MiniMapTexture.cpp =================================================================== --- source/graphics/MiniMapTexture.cpp +++ source/graphics/MiniMapTexture.cpp @@ -219,7 +219,7 @@ Renderer::Backend::Sampler::AddressMode::CLAMP_TO_EDGE); // Create terrain texture - m_TerrainTexture = Renderer::Backend::GL::CTexture::Create2D( + m_TerrainTexture = deviceCommandContext->GetDevice()->CreateTexture2D("MiniMapTerrainTexture", Renderer::Backend::Format::R8G8B8A8, textureSize, textureSize, defaultSamplerDesc); // Initialise texture with solid black, for the areas we don't @@ -234,7 +234,7 @@ m_TerrainData = std::make_unique((m_MapSize - 1) * (m_MapSize - 1)); - m_FinalTexture = Renderer::Backend::GL::CTexture::Create2D( + m_FinalTexture = deviceCommandContext->GetDevice()->CreateTexture2D("MiniMapFinalTexture", Renderer::Backend::Format::R8G8B8A8, FINAL_TEXTURE_SIZE, FINAL_TEXTURE_SIZE, defaultSamplerDesc); m_FinalTextureFramebuffer = Renderer::Backend::GL::CFramebuffer::Create( Index: source/graphics/ObjectEntry.cpp =================================================================== --- source/graphics/ObjectEntry.cpp +++ source/graphics/ObjectEntry.cpp @@ -84,7 +84,7 @@ for (const CObjectBase::Samp& samp : m_Samplers) { CTextureProperties textureProps(samp.m_SamplerFile); - textureProps.SetWrap(GL_CLAMP_TO_BORDER); + textureProps.SetAddressMode(Renderer::Backend::Sampler::AddressMode::CLAMP_TO_BORDER); CTexturePtr texture = g_Renderer.GetTextureManager().CreateTexture(textureProps); // TODO: Should check which renderpath is selected and only preload the necessary textures. texture->Prefetch(); @@ -141,7 +141,7 @@ for (const CObjectBase::Samp& samp : m_Samplers) { CTextureProperties textureProps(samp.m_SamplerFile); - textureProps.SetWrap(GL_CLAMP_TO_EDGE); + textureProps.SetAddressMode(Renderer::Backend::Sampler::AddressMode::CLAMP_TO_EDGE); CTexturePtr texture = g_Renderer.GetTextureManager().CreateTexture(textureProps); // if we've loaded this model we're probably going to render it soon, so prefetch its texture. // All textures are prefetched even in the fixed pipeline, including the normal maps etc. Index: source/graphics/Overlay.cpp =================================================================== --- source/graphics/Overlay.cpp +++ source/graphics/Overlay.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2019 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 @@ -43,12 +43,16 @@ void SOverlayTexturedLine::CreateOverlayTexture(const SOverlayDescriptor* overlayDescriptor) { CTextureProperties texturePropsBase(overlayDescriptor->m_LineTexture.c_str()); - texturePropsBase.SetWrap(GL_CLAMP_TO_BORDER, GL_CLAMP_TO_EDGE); - texturePropsBase.SetMaxAnisotropy(4.f); + texturePropsBase.SetAddressMode( + Renderer::Backend::Sampler::AddressMode::CLAMP_TO_BORDER, + Renderer::Backend::Sampler::AddressMode::CLAMP_TO_EDGE); + texturePropsBase.SetAnisotropicFilter(true); CTextureProperties texturePropsMask(overlayDescriptor->m_LineTextureMask.c_str()); - texturePropsMask.SetWrap(GL_CLAMP_TO_BORDER, GL_CLAMP_TO_EDGE); - texturePropsMask.SetMaxAnisotropy(4.f); + texturePropsMask.SetAddressMode( + Renderer::Backend::Sampler::AddressMode::CLAMP_TO_BORDER, + Renderer::Backend::Sampler::AddressMode::CLAMP_TO_EDGE); + texturePropsMask.SetAnisotropicFilter(true); m_AlwaysVisible = false; m_Closed = true; Index: source/graphics/ParticleEmitter.h =================================================================== --- source/graphics/ParticleEmitter.h +++ source/graphics/ParticleEmitter.h @@ -21,6 +21,7 @@ #include "graphics/ModelAbstract.h" #include "graphics/ParticleEmitterType.h" #include "maths/Quaternion.h" +#include "renderer/backend/gl/DeviceCommandContext.h" #include "renderer/VertexArray.h" #include @@ -120,7 +121,9 @@ /** * Bind rendering state (textures and blend modes). */ - void Bind(const CShaderProgramPtr& shader); + void Bind( + Renderer::Backend::GL::CDeviceCommandContext* deviceCommandContext, + const CShaderProgramPtr& shader); /** * Draw the vertex array. Index: source/graphics/ParticleEmitter.cpp =================================================================== --- source/graphics/ParticleEmitter.cpp +++ source/graphics/ParticleEmitter.cpp @@ -178,8 +178,12 @@ m_VertexArray.PrepareForRendering(); } -void CParticleEmitter::Bind(const CShaderProgramPtr& shader) +void CParticleEmitter::Bind( + Renderer::Backend::GL::CDeviceCommandContext* deviceCommandContext, + const CShaderProgramPtr& shader) { + m_Type->m_Texture->UploadBackendTextureIfNeeded(deviceCommandContext); + CLOSTexture& los = g_Renderer.GetSceneRenderer().GetScene().GetLOSTexture(); shader->BindTexture(str_losTex, los.GetTextureSmooth()); shader->Uniform(str_losTransform, los.GetTextureMatrix()[0], los.GetTextureMatrix()[12], 0.f, 0.f); @@ -189,7 +193,7 @@ shader->Uniform(str_fogColor, lightEnv.m_FogColor); shader->Uniform(str_fogParams, lightEnv.m_FogFactor, lightEnv.m_FogMax, 0.f, 0.f); - shader->BindTexture(str_baseTex, m_Type->m_Texture); + shader->BindTexture(str_baseTex, m_Type->m_Texture->GetBackendTexture()); } void CParticleEmitter::RenderArray(const CShaderProgramPtr& shader) Index: source/graphics/ParticleEmitterType.cpp =================================================================== --- source/graphics/ParticleEmitterType.cpp +++ source/graphics/ParticleEmitterType.cpp @@ -394,7 +394,8 @@ if (Child.GetNodeName() == el_texture) { CTextureProperties textureProps(Child.GetText().FromUTF8()); - textureProps.SetWrap(GL_CLAMP_TO_EDGE); + textureProps.SetAddressMode( + Renderer::Backend::Sampler::AddressMode::CLAMP_TO_EDGE); m_Texture = g_Renderer.GetTextureManager().CreateTexture(textureProps); } else if (Child.GetNodeName() == el_blend) Index: source/graphics/ShaderProgram.h =================================================================== --- source/graphics/ShaderProgram.h +++ source/graphics/ShaderProgram.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 @@ -19,7 +19,6 @@ #define INCLUDED_SHADERPROGRAM #include "graphics/ShaderProgramPtr.h" -#include "graphics/Texture.h" #include "lib/ogl.h" #include "lib/file/vfs/vfs_path.h" #include "renderer/backend/gl/Texture.h" @@ -143,8 +142,6 @@ virtual Binding GetTextureBinding(texture_id_t id) = 0; // Variants of texture binding: - void BindTexture(texture_id_t id, const CTexturePtr& tex); - void BindTexture(Binding id, const CTexturePtr& tex); void BindTexture(texture_id_t id, const Renderer::Backend::GL::CTexture* tex); void BindTexture(Binding id, const Renderer::Backend::GL::CTexture* tex); Index: source/graphics/ShaderProgram.cpp =================================================================== --- source/graphics/ShaderProgram.cpp +++ source/graphics/ShaderProgram.cpp @@ -24,11 +24,12 @@ #include "graphics/ShaderManager.h" #include "graphics/TextureManager.h" #include "lib/timer.h" -#include "lib/res/graphics/ogl_tex.h" #include "maths/Matrix3D.h" #include "maths/Vector3D.h" #include "ps/CLogger.h" #include "ps/Filesystem.h" +#include "renderer/backend/gl/DeviceCommandContext.h" +#include "renderer/Renderer.h" #include @@ -171,8 +172,7 @@ int index = fPair.first; if (index != -1) { - glActiveTextureARB(GL_TEXTURE0 + index); - glBindTexture(fPair.second, tex); + g_Renderer.GetDeviceCommandContext()->BindTexture(index, fPair.second, tex); } } @@ -181,8 +181,7 @@ int index = id.second; if (index != -1) { - glActiveTextureARB(GL_TEXTURE0 + index); - glBindTexture(id.first, tex); + g_Renderer.GetDeviceCommandContext()->BindTexture(index, id.first, tex); } } @@ -533,8 +532,7 @@ if (it == m_Samplers.end()) return; - glActiveTexture(GL_TEXTURE0 + it->second.second); - glBindTexture(it->second.first, tex); + g_Renderer.GetDeviceCommandContext()->BindTexture(it->second.second, it->second.first, tex); } void BindTexture(Binding id, GLuint tex) override @@ -542,8 +540,7 @@ if (id.second == -1) return; - glActiveTexture(GL_TEXTURE0 + id.second); - glBindTexture(id.first, tex); + g_Renderer.GetDeviceCommandContext()->BindTexture(id.second, id.first, tex); } Binding GetUniformBinding(uniform_id_t id) override @@ -720,20 +717,6 @@ return m_StreamFlags; } -void CShaderProgram::BindTexture(texture_id_t id, const CTexturePtr& tex) -{ - GLuint h; - ogl_tex_get_texture_id(tex->GetHandle(), &h); - BindTexture(id, h); -} - -void CShaderProgram::BindTexture(Binding id, const CTexturePtr& tex) -{ - GLuint h; - ogl_tex_get_texture_id(tex->GetHandle(), &h); - BindTexture(id, h); -} - void CShaderProgram::BindTexture(texture_id_t id, const Renderer::Backend::GL::CTexture* tex) { BindTexture(id, tex->GetHandle()); Index: source/graphics/TerrainTextureEntry.cpp =================================================================== --- source/graphics/TerrainTextureEntry.cpp +++ source/graphics/TerrainTextureEntry.cpp @@ -125,12 +125,8 @@ for (size_t i = 0; i < samplers.size(); ++i) { CTextureProperties texture(samplers[i].second); - texture.SetWrap(GL_REPEAT); - - // TODO: anisotropy should probably be user-configurable, but we want it to be - // at least 2 for terrain else the ground looks very blurry when you tilt the - // camera upwards - texture.SetMaxAnisotropy(2.0f); + texture.SetAddressMode(Renderer::Backend::Sampler::AddressMode::REPEAT); + texture.SetAnisotropicFilter(true); if (CRenderer::IsInitialised()) { Index: source/graphics/TerrainTextureManager.cpp =================================================================== --- source/graphics/TerrainTextureManager.cpp +++ source/graphics/TerrainTextureManager.cpp @@ -29,7 +29,9 @@ #include "lib/timer.h" #include "ps/CLogger.h" #include "ps/Filesystem.h" +#include "ps/VideoMode.h" #include "ps/XML/Xeromyces.h" +#include "renderer/backend/gl/Device.h" #include "renderer/Renderer.h" #include @@ -291,7 +293,7 @@ ignore_result(da_free(&da)); #endif - result.m_CompositeAlphaMap = Renderer::Backend::GL::CTexture::Create2D( + result.m_CompositeAlphaMap = g_VideoMode.GetBackendDevice()->CreateTexture2D("CompositeAlphaMap", Renderer::Backend::Format::A8, totalWidth, totalHeight, Renderer::Backend::Sampler::MakeDefaultSampler( Renderer::Backend::Sampler::Filter::LINEAR, @@ -313,9 +315,10 @@ 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()); + Renderer::Backend::GL::CTexture* texture = alphaMap.m_CompositeAlphaMap.get(); + deviceCommandContext->UploadTexture( + texture, Renderer::Backend::Format::A8, alphaMap.m_CompositeDataToUpload.get(), + texture->GetWidth() * texture->GetHeight()); alphaMap.m_CompositeDataToUpload.reset(); } Index: source/graphics/TerritoryTexture.cpp =================================================================== --- source/graphics/TerritoryTexture.cpp +++ source/graphics/TerritoryTexture.cpp @@ -23,6 +23,8 @@ #include "graphics/Terrain.h" #include "lib/bits.h" #include "ps/Profile.h" +#include "renderer/backend/gl/Device.h" +#include "renderer/backend/gl/DeviceCommandContext.h" #include "renderer/Renderer.h" #include "simulation2/Simulation2.h" #include "simulation2/helpers/Grid.h" @@ -84,7 +86,7 @@ const uint32_t textureSize = round_up_to_pow2(static_cast(m_MapSize)); - m_Texture = Renderer::Backend::GL::CTexture::Create2D( + m_Texture = deviceCommandContext->GetDevice()->CreateTexture2D("TerritoryTexture", Renderer::Backend::Format::R8G8B8A8, textureSize, textureSize, Renderer::Backend::Sampler::MakeDefaultSampler( Renderer::Backend::Sampler::Filter::LINEAR, @@ -95,7 +97,8 @@ std::unique_ptr texData = std::make_unique(textureSize * textureSize * 4); memset(texData.get(), 0x00, textureSize * textureSize * 4); deviceCommandContext->UploadTexture( - m_Texture.get(), Renderer::Backend::Format::R8G8B8A8, texData.get(), textureSize * textureSize * 4); + m_Texture.get(), Renderer::Backend::Format::R8G8B8A8, + texData.get(), textureSize * textureSize * 4); texData.reset(); { Index: source/graphics/TextRenderer.h =================================================================== --- source/graphics/TextRenderer.h +++ source/graphics/TextRenderer.h @@ -23,6 +23,7 @@ #include "maths/Rect.h" #include "maths/Vector2D.h" #include "ps/CStrIntern.h" +#include "renderer/backend/gl/DeviceCommandContext.h" #include @@ -102,7 +103,9 @@ /** * Render all of the previously printed text calls. */ - void Render(const CShaderProgramPtr& shader, const CMatrix3D& transform); + void Render( + Renderer::Backend::GL::CDeviceCommandContext* deviceCommandContext, + const CShaderProgramPtr& shader, const CMatrix3D& transform); private: friend struct SBatchCompare; Index: source/graphics/TextRenderer.cpp =================================================================== --- source/graphics/TextRenderer.cpp +++ source/graphics/TextRenderer.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 @@ -22,6 +22,7 @@ #include "graphics/Font.h" #include "graphics/FontManager.h" #include "graphics/ShaderProgram.h" +#include "graphics/TextureManager.h" #include "lib/ogl.h" #include "maths/Matrix3D.h" #include "ps/CStrIntern.h" @@ -201,7 +202,9 @@ } }; -void CTextRenderer::Render(const CShaderProgramPtr& shader, const CMatrix3D& transform) +void CTextRenderer::Render( + Renderer::Backend::GL::CDeviceCommandContext* deviceCommandContext, + const CShaderProgramPtr& shader, const CMatrix3D& transform) { std::vector indexes; std::vector vertexes; @@ -232,7 +235,8 @@ if (lastTexture != batch.font->GetTexture().get()) { lastTexture = batch.font->GetTexture().get(); - shader->BindTexture(str_tex, batch.font->GetTexture()); + lastTexture->UploadBackendTextureIfNeeded(deviceCommandContext); + shader->BindTexture(str_tex, lastTexture->GetBackendTexture()); } CMatrix3D translation; Index: source/graphics/TextureManager.h =================================================================== --- source/graphics/TextureManager.h +++ source/graphics/TextureManager.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,8 +20,9 @@ #include "graphics/Texture.h" #include "lib/file/vfs/vfs.h" -#include "lib/ogl.h" -#include "lib/res/handle.h" +#include "lib/tex/tex.h" +#include "renderer/backend/gl/DeviceCommandContext.h" +#include "renderer/backend/gl/Texture.h" #include @@ -92,17 +93,17 @@ * Returns a magenta texture. Use this for highlighting errors * (e.g. missing terrain textures). */ - CTexturePtr GetErrorTexture(); + const CTexturePtr& GetErrorTexture(); /** * Returns a single color RGBA texture with CColor(1.0f, 1.0f, 1.0f, 1.0f). */ - CTexturePtr GetWhiteTexture(); + const CTexturePtr& GetWhiteTexture(); /** * Returns a single color RGBA texture with CColor(0.0f, 0.0f, 0.0f, 0.0f). */ - CTexturePtr GetTransparentTexture(); + const CTexturePtr& GetTransparentTexture(); /** * Work on asynchronous texture loading operations, if any. @@ -113,6 +114,12 @@ bool MakeProgress(); /** + * Work on asynchronous texture uploading operations, if any. + * Returns true if it did any work. Mostly the same as MakeProgress. + */ + bool MakeUploadProgress(Renderer::Backend::GL::CDeviceCommandContext* deviceCommandContext); + + /** * Synchronously converts and compresses and saves the texture, * and returns the output path (minus a "cache/" prefix). This * is intended for pre-caching textures in release archives. @@ -131,6 +138,11 @@ */ size_t GetBytesUploaded() const; + /** + * Should be called on any quality or anisotropic change. + */ + void OnQualityChanged(); + private: CTextureManagerImpl* m; }; @@ -150,38 +162,41 @@ /** * Use the given texture name, and default GL parameters. */ - explicit CTextureProperties(const VfsPath& path) : - m_Path(path), m_Filter(GL_LINEAR_MIPMAP_LINEAR), - m_WrapS(GL_REPEAT), m_WrapT(GL_REPEAT), m_Aniso(1.0f), m_Format(0) + explicit CTextureProperties(const VfsPath& path) + : m_Path(path) { } - /** - * Set min/mag filter mode (typically GL_LINEAR_MIPMAP_LINEAR, GL_NEAREST, etc). - */ - void SetFilter(GLint filter) { m_Filter = filter; } - - /** - * Set wrapping mode (typically GL_REPEAT, GL_CLAMP_TO_EDGE, etc). - */ - void SetWrap(GLint wrap) { m_WrapS = wrap; m_WrapT = wrap; } + CTextureProperties( + const VfsPath& path, const Renderer::Backend::Format formatOverride) + : m_Path(path), m_FormatOverride(formatOverride) + { + } /** - * Set wrapping mode (typically GL_REPEAT, GL_CLAMP_TO_EDGE, etc), - * separately for S and T. + * Set sampler address mode. */ - void SetWrap(GLint wrap_s, GLint wrap_t) { m_WrapS = wrap_s; m_WrapT = wrap_t; } + void SetAddressMode(const Renderer::Backend::Sampler::AddressMode addressMode) + { + m_AddressModeU = m_AddressModeV = addressMode; + } /** - * Set maximum anisotropy value. Must be >= 1.0. Should be a power of 2. + * Set sampler address mode separately for different coordinates. */ - void SetMaxAnisotropy(float aniso) { m_Aniso = aniso; } + void SetAddressMode( + const Renderer::Backend::Sampler::AddressMode addressModeU, + const Renderer::Backend::Sampler::AddressMode addressModeV) + { + m_AddressModeU = addressModeU; + m_AddressModeV = addressModeV; + } /** - * Set GL texture upload format, to override the default. - * Typically GL_ALPHA or GL_LUMINANCE for 8-bit textures. + * The value of max anisotropy is set by options. Though it might make sense + * to add an override. */ - void SetFormatOverride(GLenum format) { m_Format = format; } + void SetAnisotropicFilter(const bool enabled) { m_AnisotropicFilterEnabled = enabled; } // TODO: rather than this static definition of texture properties // (especially anisotropy), maybe we want something that can be more @@ -200,14 +215,20 @@ // // or something a bit like that. + void SetIgnoreQuality(bool ignore) { m_IgnoreQuality = ignore; } + private: // Must update TPhash, TPequal_to when changing these fields VfsPath m_Path; - GLint m_Filter; - GLint m_WrapS; - GLint m_WrapT; - float m_Aniso; - GLenum m_Format; + + Renderer::Backend::Sampler::AddressMode m_AddressModeU = + Renderer::Backend::Sampler::AddressMode::REPEAT; + Renderer::Backend::Sampler::AddressMode m_AddressModeV = + Renderer::Backend::Sampler::AddressMode::REPEAT; + bool m_AnisotropicFilterEnabled = false; + Renderer::Backend::Format m_FormatOverride = + Renderer::Backend::Format::UNDEFINED; + bool m_IgnoreQuality = false; }; /** @@ -218,19 +239,8 @@ */ class CTexture { - friend class CTextureManagerImpl; - friend class SingleColorTexture; - friend struct TextureCacheCmp; - friend struct TPequal_to; - friend struct TPhash; - - // Only the texture manager can create these - explicit CTexture(Handle handle, const CTextureProperties& props, CTextureManagerImpl* textureManager); - NONCOPYABLE(CTexture); - public: - ~CTexture(); /** @@ -261,29 +271,37 @@ size_t GetUploadedSize() const; /** - * Bind the texture to the given GL texture unit. + * Uploads a texture data to a backend texture if successfully loaded. * If the texture data hasn't been loaded yet, this may wait a short while to * load it. If loading takes too long then it will return sooner and the data will * be loaded in a background thread, so this does not guarantee the texture really - * will be loaded. + * will be uploaded. */ - void Bind(size_t unit = 0); + void UploadBackendTextureIfNeeded( + Renderer::Backend::GL::CDeviceCommandContext* deviceCommandContext); /** - * Returns a ogl_tex handle, for later binding. See comments from Bind(). + * Returns a backend texture if successfully uploaded, else fallback. */ - Handle GetHandle(); + Renderer::Backend::GL::CTexture* GetBackendTexture(); + const Renderer::Backend::GL::CTexture* GetBackendTexture() const; /** - * Attempt to load the texture data quickly, as with Bind(). - * Returns whether the texture data is currently loaded. + * Attempt to load the texture data quickly, as with + * GetUploadedBackendTextureIfNeeded(). Returns whether the texture data is + * currently loaded (but not uploaded). */ bool TryLoad(); /** * Returns whether the texture data is currently loaded. */ - bool IsLoaded(); + bool IsLoaded() const; + + /** + * Returns whether the texture data is currently uploaded. + */ + bool IsUploaded() const; /** * Activate the prefetching optimisation for this texture. @@ -294,25 +312,42 @@ void Prefetch(); private: - /** - * Replace the Handle stored by this object. - * If takeOwnership is true, it will not increment the Handle's reference count. - */ - void SetHandle(Handle handle, bool takeOwnership = false); + friend class CTextureManagerImpl; + friend class SingleColorTexture; + friend struct TextureCacheCmp; + friend struct TPequal_to; + friend struct TPhash; + + // Only the texture manager can create these + explicit CTexture( + std::unique_ptr texture, + Renderer::Backend::GL::CTexture* fallback, + const CTextureProperties& props, CTextureManagerImpl* textureManager); + + void ResetBackendTexture( + std::unique_ptr backendTexture, + Renderer::Backend::GL::CTexture* fallbackBackendTexture); const CTextureProperties m_Properties; - Handle m_Handle; + std::unique_ptr m_BackendTexture; + // It's possible to m_FallbackBackendTexture references m_BackendTexture. + Renderer::Backend::GL::CTexture* m_FallbackBackendTexture = nullptr; u32 m_BaseColor; + std::unique_ptr m_TextureData; + size_t m_UploadedSize = 0; + uint32_t m_BaseLevelOffset = 0; - enum { + enum + { UNLOADED, // loading has not started PREFETCH_NEEDS_LOADING, // was prefetched; currently waiting to try loading from cache PREFETCH_NEEDS_CONVERTING, // was prefetched; currently waiting to be sent to the texture converter PREFETCH_IS_CONVERTING, // was prefetched; currently being processed by the texture converter HIGH_NEEDS_CONVERTING, // high-priority; currently waiting to be sent to the texture converter HIGH_IS_CONVERTING, // high-priority; currently being processed by the texture converter - LOADED // loading has completed (successfully or not) + LOADED, // loading texture data has completed (successfully or not) + UPLOADED // uploading to backend has completed (successfully or not) } m_State; CTextureManagerImpl* m_TextureManager; Index: source/graphics/TextureManager.cpp =================================================================== --- source/graphics/TextureManager.cpp +++ source/graphics/TextureManager.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 @@ -24,89 +24,162 @@ #include "lib/allocators/shared_ptr.h" #include "lib/file/vfs/vfs_tree.h" #include "lib/hash.h" -#include "lib/res/graphics/ogl_tex.h" -#include "lib/res/h_mgr.h" #include "lib/timer.h" #include "maths/MD5.h" #include "ps/CacheLoader.h" #include "ps/CLogger.h" +#include "ps/ConfigDB.h" #include "ps/Filesystem.h" #include "ps/Profile.h" +#include "ps/VideoMode.h" +#include "renderer/backend/gl/Device.h" +#include "renderer/Renderer.h" #include #include #include +#include #include #include +namespace +{ + +Renderer::Backend::Format ChooseFormatAndTransformTextureDataIfNeeded(Tex& textureData, const bool hasS3TC) +{ + const bool alpha = (textureData.m_Flags & TEX_ALPHA) != 0; + const bool grey = (textureData.m_Flags & TEX_GREY) != 0; + const size_t dxt = textureData.m_Flags & TEX_DXT; + + // Some backends don't support BGR as an internal format (like GLES). + // TODO: add a check that the format is internally supported. + if ((textureData.m_Flags & TEX_BGR) != 0) + { + LOGWARNING("Using slow path to convert BGR texture."); + textureData.transform_to(textureData.m_Flags & ~TEX_BGR); + } + + if (dxt) + { + if (hasS3TC) + { + switch (dxt) + { + case DXT1A: + return Renderer::Backend::Format::BC1_RGBA; + case 1: + return Renderer::Backend::Format::BC1_RGB; + case 3: + return Renderer::Backend::Format::BC2; + case 5: + return Renderer::Backend::Format::BC3; + default: + LOGERROR("Unknown DXT compression."); + return Renderer::Backend::Format::UNDEFINED; + } + } + else + textureData.transform_to(textureData.m_Flags & ~TEX_DXT); + } + + switch (textureData.m_Bpp) + { + case 8: + ENSURE(grey); + return Renderer::Backend::Format::L8; + case 24: + ENSURE(!alpha); + return Renderer::Backend::Format::R8G8B8; + case 32: + ENSURE(alpha); + return Renderer::Backend::Format::R8G8B8A8; + default: + LOGERROR("Unsupported BPP: %zu", textureData.m_Bpp); + } + + return Renderer::Backend::Format::UNDEFINED; +} + +} // anonymous namespace + class SingleColorTexture { public: - SingleColorTexture(const CColor& color, PIVFS vfs, const VfsPath& pathPlaceholder, const bool disableGL, CTextureManagerImpl* textureManager) - : m_Handle(0) + SingleColorTexture(const CColor& color, const VfsPath& pathPlaceholder, + const bool disableGL, CTextureManagerImpl* textureManager) + : m_Color(color) { if (disableGL) return; - const SColor4ub color32 = color.AsSColor4ub(); - // Construct 1x1 32-bit texture - std::shared_ptr data(new u8[4], ArrayDeleter()); - data.get()[0] = color32.R; - data.get()[1] = color32.G; - data.get()[2] = color32.B; - data.get()[3] = color32.A; - - Tex t; - ignore_result(t.wrap(1, 1, 32, TEX_ALPHA, data, 0)); - - m_Handle = ogl_tex_wrap(&t, vfs, pathPlaceholder); - ignore_result(ogl_tex_set_filter(m_Handle, GL_LINEAR)); - if (!disableGL) - ignore_result(ogl_tex_upload(m_Handle)); + std::stringstream textureName; + textureName << "SingleColorTexture ("; + textureName << "R: " << m_Color.r << ","; + textureName << "G: " << m_Color.g << ","; + textureName << "B: " << m_Color.b << ","; + textureName << "A: " << m_Color.a << ")"; + + std::unique_ptr backendTexture = + g_VideoMode.GetBackendDevice()->CreateTexture2D( + textureName.str().c_str(), + Renderer::Backend::Format::R8G8B8A8, + 1, 1, Renderer::Backend::Sampler::MakeDefaultSampler( + Renderer::Backend::Sampler::Filter::LINEAR, + Renderer::Backend::Sampler::AddressMode::REPEAT)); + Renderer::Backend::GL::CTexture* fallback = backendTexture.get(); CTextureProperties props(pathPlaceholder); - m_Texture = CTexturePtr(new CTexture(m_Handle, props, textureManager)); - m_Texture->m_State = CTexture::LOADED; + m_Texture = CTexturePtr(new CTexture( + std::move(backendTexture), fallback, props, textureManager)); + m_Texture->m_State = CTexture::UPLOADED; m_Texture->m_Self = m_Texture; } - ~SingleColorTexture() - { - ignore_result(ogl_tex_free(m_Handle)); - } - - CTexturePtr GetTexture() + const CTexturePtr& GetTexture() { return m_Texture; } - Handle GetHandle() + void Upload(Renderer::Backend::GL::CDeviceCommandContext* deviceCommandContext) { - return m_Handle; + if (!m_Texture || !m_Texture->GetBackendTexture()) + return; + + const SColor4ub color32 = m_Color.AsSColor4ub(); + // Construct 1x1 32-bit texture + const u8 data[4] = + { + color32.R, + color32.G, + color32.B, + color32.A + }; + deviceCommandContext->UploadTexture(m_Texture->GetBackendTexture(), + Renderer::Backend::Format::R8G8B8A8, data, std::size(data)); } private: - Handle m_Handle; CTexturePtr m_Texture; + CColor m_Color; }; struct TPhash { - std::size_t operator()(CTextureProperties const& a) const + std::size_t operator()(const CTextureProperties& textureProperties) const { std::size_t seed = 0; - hash_combine(seed, m_PathHash(a.m_Path)); - hash_combine(seed, a.m_Filter); - hash_combine(seed, a.m_WrapS); - hash_combine(seed, a.m_WrapT); - hash_combine(seed, a.m_Aniso); - hash_combine(seed, a.m_Format); + hash_combine(seed, m_PathHash(textureProperties.m_Path)); + hash_combine(seed, textureProperties.m_AddressModeU); + hash_combine(seed, textureProperties.m_AddressModeV); + hash_combine(seed, textureProperties.m_AnisotropicFilterEnabled); + hash_combine(seed, textureProperties.m_FormatOverride); + hash_combine(seed, textureProperties.m_IgnoreQuality); return seed; } - std::size_t operator()(CTexturePtr const& a) const + std::size_t operator()(const CTexturePtr& texture) const { - return (*this)(a->m_Properties); + return this->operator()(texture->m_Properties); } private: @@ -115,15 +188,20 @@ struct TPequal_to { - bool operator()(CTextureProperties const& a, CTextureProperties const& b) const + bool operator()(const CTextureProperties& lhs, const CTextureProperties& rhs) const { - return a.m_Path == b.m_Path && a.m_Filter == b.m_Filter - && a.m_WrapS == b.m_WrapS && a.m_WrapT == b.m_WrapT - && a.m_Aniso == b.m_Aniso && a.m_Format == b.m_Format; + return + lhs.m_Path == rhs.m_Path && + lhs.m_AddressModeU == rhs.m_AddressModeU && + lhs.m_AddressModeV == rhs.m_AddressModeV && + lhs.m_AnisotropicFilterEnabled == rhs.m_AnisotropicFilterEnabled && + lhs.m_FormatOverride == rhs.m_FormatOverride && + lhs.m_IgnoreQuality == rhs.m_IgnoreQuality; } - bool operator()(CTexturePtr const& a, CTexturePtr const& b) const + + bool operator()(const CTexturePtr& lhs, const CTexturePtr& rhs) const { - return (*this)(a->m_Properties, b->m_Properties); + return this->operator()(lhs->m_Properties, rhs->m_Properties); } }; @@ -134,13 +212,23 @@ CTextureManagerImpl(PIVFS vfs, bool highQuality, bool disableGL) : m_VFS(vfs), m_CacheLoader(vfs, L".dds"), m_DisableGL(disableGL), m_TextureConverter(vfs, highQuality), - m_DefaultTexture(CColor(0.25f, 0.25f, 0.25f, 1.0f), vfs, L"(default texture)", disableGL, this), - m_ErrorTexture(CColor(1.0f, 0.0f, 1.0f, 1.0f), vfs, L"(error texture)", disableGL, this), - m_WhiteTexture(CColor(1.0f, 1.0f, 1.0f, 1.0f), vfs, L"(white texture)", disableGL, this), - m_TransparentTexture(CColor(0.0f, 0.0f, 0.0f, 0.0f), vfs, L"(transparent texture)", disableGL, this) + m_DefaultTexture(CColor(0.25f, 0.25f, 0.25f, 1.0f), L"(default texture)", disableGL, this), + m_ErrorTexture(CColor(1.0f, 0.0f, 1.0f, 1.0f), L"(error texture)", disableGL, this), + m_WhiteTexture(CColor(1.0f, 1.0f, 1.0f, 1.0f), L"(white texture)", disableGL, this), + m_TransparentTexture(CColor(0.0f, 0.0f, 0.0f, 0.0f), L"(transparent texture)", disableGL, this) { // Allow hotloading of textures RegisterFileReloadFunc(ReloadChangedFileCB, this); + + if (disableGL) + return; + + Renderer::Backend::GL::CDevice* backendDevice = g_VideoMode.GetBackendDevice(); + m_HasS3TC = + backendDevice->IsFormatSupported(Renderer::Backend::Format::BC1_RGB) && + backendDevice->IsFormatSupported(Renderer::Backend::Format::BC1_RGBA) && + backendDevice->IsFormatSupported(Renderer::Backend::Format::BC2) && + backendDevice->IsFormatSupported(Renderer::Backend::Format::BC3); } ~CTextureManagerImpl() @@ -148,17 +236,17 @@ UnregisterFileReloadFunc(ReloadChangedFileCB, this); } - CTexturePtr GetErrorTexture() + const CTexturePtr& GetErrorTexture() { return m_ErrorTexture.GetTexture(); } - CTexturePtr GetWhiteTexture() + const CTexturePtr& GetWhiteTexture() { return m_WhiteTexture.GetTexture(); } - CTexturePtr GetTransparentTexture() + const CTexturePtr& GetTransparentTexture() { return m_TransparentTexture.GetTexture(); } @@ -169,7 +257,9 @@ CTexturePtr CreateTexture(const CTextureProperties& props) { // Construct a new default texture with the given properties to use as the search key - CTexturePtr texture(new CTexture(m_DefaultTexture.GetHandle(), props, this)); + CTexturePtr texture(new CTexture( + nullptr, m_DisableGL ? nullptr : m_DefaultTexture.GetTexture()->GetBackendTexture(), + props, this)); // Try to find an existing texture with the given properties TextureCache::iterator it = m_TextureCache.find(texture); @@ -196,60 +286,104 @@ PROFILE2("load texture"); PROFILE2_ATTR("name: %ls", path.string().c_str()); - Handle h = ogl_tex_load(m_VFS, path, RES_UNIQUE); - if (h <= 0) + std::shared_ptr fileData; + size_t fileSize; + texture->m_TextureData = std::make_unique(); + Tex& textureData = *texture->m_TextureData; + if (g_VFS->LoadFile(path, fileData, fileSize) != INFO::OK || + textureData.decode(fileData, fileSize) != INFO::OK) { LOGERROR("Texture failed to load; \"%s\"", texture->m_Properties.m_Path.string8()); - - // Replace with error texture to make it obvious - texture->SetHandle(m_ErrorTexture.GetHandle()); + texture->ResetBackendTexture( + nullptr, m_ErrorTexture.GetTexture()->GetBackendTexture()); return; } - // Get some flags for later use - size_t flags = 0; - ignore_result(ogl_tex_get_format(h, &flags, NULL)); - // Initialise base color from the texture - ignore_result(ogl_tex_get_average_color(h, &texture->m_BaseColor)); - - // Set GL upload properties - ignore_result(ogl_tex_set_wrap(h, texture->m_Properties.m_WrapS, texture->m_Properties.m_WrapT)); - ignore_result(ogl_tex_set_anisotropy(h, texture->m_Properties.m_Aniso)); + texture->m_BaseColor = textureData.get_average_color(); - // Prevent ogl_tex automatically generating mipmaps (which is slow and unwanted), - // by avoiding mipmapped filters unless the source texture already has mipmaps - GLint filter = texture->m_Properties.m_Filter; - if (!(flags & TEX_MIPMAPS)) + Renderer::Backend::Format format = Renderer::Backend::Format::UNDEFINED; + if (texture->m_Properties.m_FormatOverride != Renderer::Backend::Format::UNDEFINED) { - switch (filter) + format = texture->m_Properties.m_FormatOverride; + // TODO: it'd be good to remove the override hack and provide information + // via XML. + ENSURE((textureData.m_Flags & TEX_DXT) == 0); + if (format == Renderer::Backend::Format::A8) { - case GL_NEAREST_MIPMAP_NEAREST: - case GL_NEAREST_MIPMAP_LINEAR: - filter = GL_NEAREST; - break; - case GL_LINEAR_MIPMAP_NEAREST: - case GL_LINEAR_MIPMAP_LINEAR: - filter = GL_LINEAR; - break; + ENSURE(textureData.m_Bpp == 8 && (textureData.m_Flags & TEX_GREY)); } + else if (format == Renderer::Backend::Format::R8G8B8A8) + { + ENSURE(textureData.m_Bpp == 32 && (textureData.m_Flags & TEX_ALPHA)); + } + else + debug_warn("Unsupported format override."); + } + else + { + format = ChooseFormatAndTransformTextureDataIfNeeded(textureData, m_HasS3TC); } - ignore_result(ogl_tex_set_filter(h, filter)); - // Upload to GL - if (!m_DisableGL && ogl_tex_upload(h, texture->m_Properties.m_Format) < 0) + if (format == Renderer::Backend::Format::UNDEFINED) { - LOGERROR("Texture failed to upload: \"%s\"", texture->m_Properties.m_Path.string8()); + LOGERROR("Texture failed to choose format; \"%s\"", texture->m_Properties.m_Path.string8()); + texture->ResetBackendTexture( + nullptr, m_ErrorTexture.GetTexture()->GetBackendTexture()); + return; + } - ogl_tex_free(h); + const uint32_t width = texture->m_TextureData->m_Width; + const uint32_t height = texture->m_TextureData->m_Height ; + const uint32_t MIPLevelCount = texture->m_TextureData->GetMIPLevels().size(); + texture->m_BaseLevelOffset = 0; + + Renderer::Backend::Sampler::Desc defaultSamplerDesc = + Renderer::Backend::Sampler::MakeDefaultSampler( + Renderer::Backend::Sampler::Filter::LINEAR, + Renderer::Backend::Sampler::AddressMode::REPEAT); + + defaultSamplerDesc.addressModeU = texture->m_Properties.m_AddressModeU; + defaultSamplerDesc.addressModeV = texture->m_Properties.m_AddressModeV; + if (texture->m_Properties.m_AnisotropicFilterEnabled) + { + int maxAnisotropy = 1; + CFG_GET_VAL("textures.maxanisotropy", maxAnisotropy); + const int allowedValues[] = {2, 4, 8, 16}; + if (std::find(std::begin(allowedValues), std::end(allowedValues), maxAnisotropy) != std::end(allowedValues)) + { + defaultSamplerDesc.anisotropyEnabled = true; + defaultSamplerDesc.maxAnisotropy = maxAnisotropy; + } + } - // Replace with error texture to make it obvious - texture->SetHandle(m_ErrorTexture.GetHandle()); - return; + if (!texture->m_Properties.m_IgnoreQuality) + { + int quality = 2; + CFG_GET_VAL("textures.quality", quality); + if (quality == 1) + { + if (MIPLevelCount > 1 && std::min(width, height) > 8) + texture->m_BaseLevelOffset += 1; + } + else if (quality == 0) + { + if (MIPLevelCount > 2 && std::min(width, height) > 16) + texture->m_BaseLevelOffset += 2; + while (std::min(width >> texture->m_BaseLevelOffset, height >> texture->m_BaseLevelOffset) > 256 && + MIPLevelCount > texture->m_BaseLevelOffset + 1) + { + texture->m_BaseLevelOffset += 1; + } + defaultSamplerDesc.mipFilter = Renderer::Backend::Sampler::Filter::NEAREST; + defaultSamplerDesc.anisotropyEnabled = false; + } } - // Let the texture object take ownership of this handle - texture->SetHandle(h, true); + texture->m_BackendTexture = g_VideoMode.GetBackendDevice()->CreateTexture2D( + texture->m_Properties.m_Path.string8().c_str(), + format, (width >> texture->m_BaseLevelOffset), (height >> texture->m_BaseLevelOffset), + defaultSamplerDesc, MIPLevelCount - texture->m_BaseLevelOffset); } /** @@ -299,7 +433,8 @@ // No source file or archive cache was found, so we can't load the // real texture at all - return the error texture instead LOGERROR("CCacheLoader failed to find archived or source file for: \"%s\"", texture->m_Properties.m_Path.string8()); - texture->SetHandle(m_ErrorTexture.GetHandle()); + texture->ResetBackendTexture( + nullptr, m_ErrorTexture.GetTexture()->GetBackendTexture()); return true; } } @@ -372,7 +507,8 @@ else { LOGERROR("Texture failed to convert: \"%s\"", texture->m_Properties.m_Path.string8()); - texture->SetHandle(m_ErrorTexture.GetHandle()); + texture->ResetBackendTexture( + nullptr, m_ErrorTexture.GetTexture()->GetBackendTexture()); } texture->m_State = CTexture::LOADED; return true; @@ -434,6 +570,21 @@ return false; } + bool MakeUploadProgress( + Renderer::Backend::GL::CDeviceCommandContext* deviceCommandContext) + { + if (!m_SingleColorTexturesUploaded) + { + m_DefaultTexture.Upload(deviceCommandContext); + m_ErrorTexture.Upload(deviceCommandContext); + m_WhiteTexture.Upload(deviceCommandContext); + m_TransparentTexture.Upload(deviceCommandContext); + m_SingleColorTexturesUploaded = true; + return true; + } + return false; + } + /** * Compute the conversion settings that apply to a given texture, by combining * the textures.xml files from its directory and all parent directories @@ -495,12 +646,14 @@ if (files != m_HotloadFiles.end()) { // Flag all textures using this file as needing reloading - for (std::set >::iterator it = files->second.begin(); it != files->second.end(); ++it) + for (std::set>::iterator it = files->second.begin(); it != files->second.end(); ++it) { if (std::shared_ptr texture = it->lock()) { texture->m_State = CTexture::UNLOADED; - texture->SetHandle(m_DefaultTexture.GetHandle()); + texture->ResetBackendTexture( + nullptr, m_DefaultTexture.GetTexture()->GetBackendTexture()); + texture->m_TextureData.reset(); } } } @@ -508,6 +661,17 @@ return INFO::OK; } + void ReloadAllTextures() + { + for (const CTexturePtr& texture : m_TextureCache) + { + texture->m_State = CTexture::UNLOADED; + texture->ResetBackendTexture( + nullptr, m_DefaultTexture.GetTexture()->GetBackendTexture()); + texture->m_TextureData.reset(); + } + } + size_t GetBytesUploaded() const { size_t size = 0; @@ -516,6 +680,11 @@ return size; } + void OnQualityChanged() + { + ReloadAllTextures(); + } + private: PIVFS m_VFS; CCacheLoader m_CacheLoader; @@ -526,6 +695,7 @@ SingleColorTexture m_ErrorTexture; SingleColorTexture m_WhiteTexture; SingleColorTexture m_TransparentTexture; + bool m_SingleColorTexturesUploaded = false; // Cache of all loaded textures using TextureCache = @@ -536,44 +706,68 @@ // Store the set of textures that need to be reloaded when the given file // (a source file or settings.xml) is modified using HotloadFilesMap = - std::unordered_map, std::owner_less > > >; + std::unordered_map, std::owner_less>>>; HotloadFilesMap m_HotloadFiles; // Cache for the conversion settings files using SettingsFilesMap = std::unordered_map>; SettingsFilesMap m_SettingsFiles; + + bool m_HasS3TC = false; }; -CTexture::CTexture(Handle handle, const CTextureProperties& props, CTextureManagerImpl* textureManager) : - m_Handle(handle), m_BaseColor(0), m_State(UNLOADED), m_Properties(props), m_TextureManager(textureManager) +CTexture::CTexture( + std::unique_ptr texture, + Renderer::Backend::GL::CTexture* fallback, + const CTextureProperties& props, CTextureManagerImpl* textureManager) : + m_BackendTexture(std::move(texture)), m_FallbackBackendTexture(fallback), + m_BaseColor(0), m_State(UNLOADED), m_Properties(props), + m_TextureManager(textureManager) { - // Add a reference to the handle (it might be shared by multiple CTextures - // so we can't take ownership of it) - if (m_Handle) - h_add_ref(m_Handle); } -CTexture::~CTexture() +CTexture::~CTexture() = default; + +void CTexture::UploadBackendTextureIfNeeded( + Renderer::Backend::GL::CDeviceCommandContext* deviceCommandContext) { - if (m_Handle) - ogl_tex_free(m_Handle); + if (IsUploaded()) + return; + + if (!IsLoaded()) + TryLoad(); + + if (!IsLoaded()) + return; + else if (!m_TextureData) + { + ResetBackendTexture(nullptr, m_TextureManager->GetErrorTexture()->GetBackendTexture()); + m_State = UPLOADED; + return; + } + + m_UploadedSize = 0; + for (uint32_t textureDataLevel = m_BaseLevelOffset, level = 0; textureDataLevel < m_TextureData->GetMIPLevels().size(); ++textureDataLevel) + { + const Tex::MIPLevel& levelData = m_TextureData->GetMIPLevels()[textureDataLevel]; + deviceCommandContext->UploadTexture(m_BackendTexture.get(), m_BackendTexture->GetFormat(), + levelData.data, levelData.dataSize, level++); + m_UploadedSize += levelData.dataSize; + } + m_TextureData.reset(); + + m_State = UPLOADED; } -void CTexture::Bind(size_t unit) +Renderer::Backend::GL::CTexture* CTexture::GetBackendTexture() { - ogl_tex_bind(GetHandle(), unit); + return m_BackendTexture && IsUploaded() ? m_BackendTexture.get() : m_FallbackBackendTexture; } -Handle CTexture::GetHandle() +const Renderer::Backend::GL::CTexture* CTexture::GetBackendTexture() const { - // TODO: TryLoad might call ogl_tex_upload which enables GL_TEXTURE_2D - // on texture unit 0, regardless of 'unit', which callers might - // not be expecting. Ideally that wouldn't happen. - - TryLoad(); - - return m_Handle; + return m_BackendTexture && IsUploaded() ? m_BackendTexture.get() : m_FallbackBackendTexture; } bool CTexture::TryLoad() @@ -591,7 +785,7 @@ } } - return (m_State == LOADED); + return m_State == LOADED; } void CTexture::Prefetch() @@ -605,42 +799,43 @@ } } -bool CTexture::IsLoaded() +bool CTexture::IsLoaded() const { - return (m_State == LOADED); + return m_State == LOADED; } -void CTexture::SetHandle(Handle handle, bool takeOwnership) +bool CTexture::IsUploaded() const { - if (handle == m_Handle) - return; - - if (!takeOwnership) - h_add_ref(handle); + return m_State == UPLOADED; +} - ogl_tex_free(m_Handle); - m_Handle = handle; +void CTexture::ResetBackendTexture( + std::unique_ptr backendTexture, + Renderer::Backend::GL::CTexture* fallbackBackendTexture) +{ + m_BackendTexture = std::move(backendTexture); + m_FallbackBackendTexture = fallbackBackendTexture; } size_t CTexture::GetWidth() const { - size_t w = 0; - ignore_result(ogl_tex_get_size(m_Handle, &w, 0, 0)); - return w; + return GetBackendTexture()->GetWidth(); } size_t CTexture::GetHeight() const { - size_t h = 0; - ignore_result(ogl_tex_get_size(m_Handle, 0, &h, 0)); - return h; + return GetBackendTexture()->GetHeight(); } bool CTexture::HasAlpha() const { - size_t flags = 0; - ignore_result(ogl_tex_get_format(m_Handle, &flags, 0)); - return (flags & TEX_ALPHA) != 0; + const Renderer::Backend::Format format = GetBackendTexture()->GetFormat(); + return + format == Renderer::Backend::Format::A8 || + format == Renderer::Backend::Format::R8G8B8A8 || + format == Renderer::Backend::Format::BC1_RGBA || + format == Renderer::Backend::Format::BC2 || + format == Renderer::Backend::Format::BC3; } u32 CTexture::GetBaseColor() const @@ -650,12 +845,9 @@ size_t CTexture::GetUploadedSize() const { - size_t size = 0; - ignore_result(ogl_tex_get_uploaded_size(m_Handle, &size)); - return size; + return m_UploadedSize; } - // CTextureManager: forward all calls to impl: CTextureManager::CTextureManager(PIVFS vfs, bool highQuality, bool disableGL) : @@ -678,17 +870,17 @@ return m->TextureExists(path); } -CTexturePtr CTextureManager::GetErrorTexture() +const CTexturePtr& CTextureManager::GetErrorTexture() { return m->GetErrorTexture(); } -CTexturePtr CTextureManager::GetWhiteTexture() +const CTexturePtr& CTextureManager::GetWhiteTexture() { return m->GetWhiteTexture(); } -CTexturePtr CTextureManager::GetTransparentTexture() +const CTexturePtr& CTextureManager::GetTransparentTexture() { return m->GetTransparentTexture(); } @@ -698,6 +890,12 @@ return m->MakeProgress(); } +bool CTextureManager::MakeUploadProgress( + Renderer::Backend::GL::CDeviceCommandContext* deviceCommandContext) +{ + return m->MakeUploadProgress(deviceCommandContext); +} + bool CTextureManager::GenerateCachedTexture(const VfsPath& path, VfsPath& outputPath) { return m->GenerateCachedTexture(path, outputPath); @@ -707,3 +905,8 @@ { return m->GetBytesUploaded(); } + +void CTextureManager::OnQualityChanged() +{ + m->OnQualityChanged(); +} Index: source/gui/CGUI.cpp =================================================================== --- source/gui/CGUI.cpp +++ source/gui/CGUI.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 @@ -1083,11 +1083,11 @@ else if (attr_name == "wrap_mode") { if (attr_value == L"repeat") - image->m_WrapMode = GL_REPEAT; + image->m_AddressMode = Renderer::Backend::Sampler::AddressMode::REPEAT; else if (attr_value == L"mirrored_repeat") - image->m_WrapMode = GL_MIRRORED_REPEAT; + image->m_AddressMode = Renderer::Backend::Sampler::AddressMode::MIRRORED_REPEAT; else if (attr_value == L"clamp_to_edge") - image->m_WrapMode = GL_CLAMP_TO_EDGE; + image->m_AddressMode = Renderer::Backend::Sampler::AddressMode::CLAMP_TO_EDGE; else LOGERROR("GUI: Error parsing '%s' (\"%s\")", attr_name, utf8_from_wstring(attr_value)); } Index: source/gui/CGUISprite.h =================================================================== --- source/gui/CGUISprite.h +++ source/gui/CGUISprite.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 @@ -26,9 +26,9 @@ #include "gui/GUIRenderer.h" #include "gui/SettingTypes/CGUISize.h" #include "gui/SettingTypes/CGUIColor.h" -#include "lib/ogl.h" #include "lib/file/vfs/vfs_path.h" #include "ps/CStr.h" +#include "renderer/backend/Sampler.h" #include #include @@ -55,7 +55,7 @@ SGUIImage() : m_FixedHAspectRatio(0.f), m_RoundCoordinates(true), - m_WrapMode(GL_REPEAT), + m_AddressMode(Renderer::Backend::Sampler::AddressMode::REPEAT), m_Effects(), m_Size(CGUISize::Full()), m_TextureSize(CGUISize::Full()) @@ -89,9 +89,9 @@ bool m_RoundCoordinates; /** - * Texture wrapping mode (GL_REPEAT, GL_CLAMP_TO_EDGE, etc) + * Texture address mode (REPEAT, CLAMP_TO_EDGE, etc). */ - GLint m_WrapMode; + Renderer::Backend::Sampler::AddressMode m_AddressMode; // Visual effects (e.g. color modulation) std::shared_ptr m_Effects; Index: source/gui/GUIRenderer.cpp =================================================================== --- source/gui/GUIRenderer.cpp +++ source/gui/GUIRenderer.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 @@ -26,8 +26,6 @@ #include "gui/GUIMatrix.h" #include "gui/SettingTypes/CGUIColor.h" #include "i18n/L10n.h" -#include "lib/ogl.h" -#include "lib/res/graphics/ogl_tex.h" #include "lib/tex/tex.h" #include "lib/utf8.h" #include "ps/CLogger.h" @@ -204,7 +202,8 @@ if (!(*cit)->m_TextureName.empty()) { CTextureProperties textureProps(g_L10n.LocalizePath((*cit)->m_TextureName)); - textureProps.SetWrap((*cit)->m_WrapMode); + textureProps.SetAddressMode((*cit)->m_AddressMode); + textureProps.SetIgnoreQuality(true); CTexturePtr texture = g_Renderer.GetTextureManager().CreateTexture(textureProps); texture->Prefetch(); hasTexture = true; @@ -323,9 +322,8 @@ // Iterate through each DrawCall, and execute whatever drawing code is being called for (DrawCalls::const_iterator cit = Calls.begin(); cit != Calls.end(); ++cit) { - // A hack to preload the handle to get a correct texture size. - GLuint h; - ogl_tex_get_texture_id(cit->m_Texture->GetHandle(), &h); + // A hack to get a correct backend texture size. + cit->m_Texture->UploadBackendTextureIfNeeded(g_Renderer.GetDeviceCommandContext()); CRect texCoords = cit->ComputeTexCoords().Scale( cit->m_Texture->GetWidth(), cit->m_Texture->GetHeight()); Index: source/gui/ObjectTypes/CMiniMap.cpp =================================================================== --- source/gui/ObjectTypes/CMiniMap.cpp +++ source/gui/ObjectTypes/CMiniMap.cpp @@ -220,7 +220,8 @@ m_FlareTextures.reserve(m_FlareTextureCount); for (u32 i = 0; i < m_FlareTextureCount; ++i) { - const CTextureProperties textureProps(fmt::sprintf(textureNumberingFormat, i).c_str()); + CTextureProperties textureProps(fmt::sprintf(textureNumberingFormat, i).c_str()); + textureProps.SetIgnoreQuality(true); m_FlareTextures.emplace_back(g_Renderer.GetTextureManager().CreateTexture(textureProps)); } } Index: source/lib/tex/tex.h =================================================================== --- source/lib/tex/tex.h +++ source/lib/tex/tex.h @@ -109,6 +109,7 @@ #include "lib/file/vfs/vfs_path.h" #include "lib/allocators/dynarray.h" +#include namespace ERR { @@ -202,12 +203,21 @@ }; /** - * stores all data describing an image. - * we try to minimize size, since this is stored in OglTex resources - * (which are big and pushing the h_mgr limit). + * Stores all data describing an image. + * TODO: rename to TextureData. **/ -struct Tex +class Tex { +public: + struct MIPLevel + { + // A pointer to the mip level image data (pixels). + u8* data; + u32 dataSize; + u32 width; + u32 height; + }; + /** * file buffer or image data. note: during the course of transforms * (which may occur when being loaded), this may be replaced with @@ -332,13 +342,7 @@ **/ u8* get_data(); - /** - * return a pointer to the mip level image data (pixels). - * - * @param level which level's data should be returned. - * @return pointer to the data. - **/ - u8* GetMipLevelData(const u32 level); + const std::vector& GetMIPLevels() const { return m_MIPLevels; } /** * return the ARGB value of the 1x1 mipmap level of the texture. @@ -356,6 +360,10 @@ **/ size_t img_size() const; +private: + void UpdateMIPLevels(); + + std::vector m_MIPLevels; }; Index: source/lib/tex/tex.cpp =================================================================== --- source/lib/tex/tex.cpp +++ source/lib/tex/tex.cpp @@ -474,12 +474,14 @@ return INFO::OK; Status ret = tex_codec_transform(this, remaining_transforms); + UpdateMIPLevels(); if(ret != INFO::OK) break; } // last chance RETURN_STATUS_IF_ERR(plain_transform(this, remaining_transforms)); + UpdateMIPLevels(); return INFO::OK; } @@ -596,6 +598,9 @@ m_Ofs = ofs; CHECK_TEX(this); + + UpdateMIPLevels(); + return INFO::OK; } @@ -632,31 +637,6 @@ return p + m_Ofs; } -u8* Tex::GetMipLevelData(const u32 level) -{ - // (can't use normal CHECK_TEX due to u8* return value) - WARN_IF_ERR(validate()); - - u8* levelData = m_Data.get(); - if (!levelData) - return nullptr; - levelData += m_Ofs; - const size_t dataPadding = (m_Flags & TEX_DXT) != 0 ? 4 : 1; - size_t levelWidth = m_Width, levelHeight = m_Height; - for (u32 currentLevel = 0; levelWidth > 1 || levelHeight > 1; ++currentLevel) - { - if (currentLevel == level) - return levelData; - - const size_t levelDataSize = round_up(levelWidth, dataPadding) * round_up(levelHeight, dataPadding) * m_Bpp / 8; - levelData += levelDataSize; - - levelWidth = std::max(levelWidth / 2, 1); - levelHeight = std::max(levelHeight / 2, 1); - } - return nullptr; -} - // returns color of 1x1 mipmap level u32 Tex::get_average_color() const { @@ -765,6 +745,8 @@ CHECK_TEX(this); + UpdateMIPLevels(); + return INFO::OK; } @@ -795,3 +777,33 @@ return INFO::OK; } + +void Tex::UpdateMIPLevels() +{ + m_MIPLevels.clear(); + + u8* levelData = m_Data.get(); + levelData += m_Ofs; + + const u32 dataPadding = (m_Flags & TEX_DXT) != 0 ? 4 : 1; + u32 levelWidth = m_Width, levelHeight = m_Height; + for (u32 level = 0; ; ++level) + { + const u32 levelDataSize = round_up(levelWidth, dataPadding) * round_up(levelHeight, dataPadding) * m_Bpp / 8; + m_MIPLevels.emplace_back(); + m_MIPLevels.back().data = levelData; + m_MIPLevels.back().dataSize = levelDataSize; + m_MIPLevels.back().width = levelWidth; + m_MIPLevels.back().height = levelHeight; + + if (!(m_Flags & TEX_MIPMAPS)) + break; + + if (levelWidth == 1 && levelHeight == 1) + break; + + levelData += levelDataSize; + levelWidth = std::max(levelWidth / 2, 1); + levelHeight = std::max(levelHeight / 2, 1); + } +} Index: source/ps/Util.h =================================================================== --- source/ps/Util.h +++ source/ps/Util.h @@ -21,7 +21,7 @@ #include "lib/os_path.h" #include "lib/file/vfs/vfs_path.h" -struct Tex; +class Tex; void WriteSystemInfo(); Index: source/renderer/DecalRData.cpp =================================================================== --- source/renderer/DecalRData.cpp +++ source/renderer/DecalRData.cpp @@ -177,7 +177,9 @@ const CMaterial::SamplersVector& samplers = material.GetSamplers(); for (const CMaterial::TextureSampler& sampler : samplers) - shader->BindTexture(sampler.Name, sampler.Sampler); + sampler.Sampler->UploadBackendTextureIfNeeded(deviceCommandContext); + for (const CMaterial::TextureSampler& sampler : samplers) + shader->BindTexture(sampler.Name, sampler.Sampler->GetBackendTexture()); material.GetStaticUniforms().BindUniforms(shader); Index: source/renderer/ModelRenderer.cpp =================================================================== --- source/renderer/ModelRenderer.cpp +++ source/renderer/ModelRenderer.cpp @@ -693,7 +693,8 @@ CTexture* newTex = samp.Sampler.get(); if (texBindings[s].Active() && newTex != currentTexs[s]) { - shader->BindTexture(texBindings[s], samp.Sampler); + newTex->UploadBackendTextureIfNeeded(deviceCommandContext); + shader->BindTexture(texBindings[s], newTex->GetBackendTexture()); currentTexs[s] = newTex; } } @@ -733,9 +734,13 @@ const double period = 1.6; const WaterManager& waterManager = g_Renderer.GetSceneRenderer().GetWaterManager(); if (waterManager.m_RenderWater && waterManager.WillRenderFancyWater()) - shader->BindTexture(str_waterTex, waterManager.m_NormalMap[waterManager.GetCurrentTextureIndex(period)]); + { + const CTexturePtr& waterTexture = waterManager.m_NormalMap[waterManager.GetCurrentTextureIndex(period)]; + waterTexture->UploadBackendTextureIfNeeded(deviceCommandContext); + shader->BindTexture(str_waterTex, waterTexture->GetBackendTexture()); + } else - shader->BindTexture(str_waterTex, g_Renderer.GetTextureManager().GetErrorTexture()); + shader->BindTexture(str_waterTex, g_Renderer.GetTextureManager().GetErrorTexture()->GetBackendTexture()); } else if (rq.first == RQUERY_SKY_CUBE) { Index: source/renderer/OverlayRenderer.h =================================================================== --- source/renderer/OverlayRenderer.h +++ source/renderer/OverlayRenderer.h @@ -107,7 +107,8 @@ * (i.e. rendered behind other objects in the normal 3D way) * and should be drawn after water (i.e. may be visible on top of the water) */ - void RenderOverlaysAfterWater(Renderer::Backend::GL::CDeviceCommandContext* deviceCommandContext); + void RenderOverlaysAfterWater( + Renderer::Backend::GL::CDeviceCommandContext* deviceCommandContext); /** * Render all the submitted overlays that should appear on top of everything @@ -136,7 +137,9 @@ * batch rendering the overlay lines according to their alwaysVisible status, as this * requires a separate shader to be used. */ - void RenderTexturedOverlayLines(const CShaderProgramPtr& shader, bool alwaysVisible); + void RenderTexturedOverlayLines( + Renderer::Backend::GL::CDeviceCommandContext* deviceCommandContext, + const CShaderProgramPtr& shader, bool alwaysVisible); /** * Helper method; batch-renders all registered quad overlays, batched by their texture for effiency. Index: source/renderer/OverlayRenderer.cpp =================================================================== --- source/renderer/OverlayRenderer.cpp +++ source/renderer/OverlayRenderer.cpp @@ -409,8 +409,6 @@ ogl_WarnIfError(); - glActiveTextureARB(GL_TEXTURE0); - CLOSTexture& los = g_Renderer.GetSceneRenderer().GetScene().GetLOSTexture(); // ---------------------------------------------------------------------------------------- @@ -439,7 +437,7 @@ shaderTexLineNormal->Uniform(str_transform, g_Renderer.GetSceneRenderer().GetViewCamera().GetViewProjection()); // batch render only the non-always-visible overlay lines using the normal shader - RenderTexturedOverlayLines(shaderTexLineNormal, false); + RenderTexturedOverlayLines(deviceCommandContext, shaderTexLineNormal, false); shaderTechTexLineNormal->EndPass(); } @@ -471,7 +469,7 @@ shaderTexLineAlwaysVisible->Uniform(str_transform, g_Renderer.GetSceneRenderer().GetViewCamera().GetViewProjection()); // batch render only the always-visible overlay lines using the LoS-ignored shader - RenderTexturedOverlayLines(shaderTexLineAlwaysVisible, true); + RenderTexturedOverlayLines(deviceCommandContext, shaderTexLineAlwaysVisible, true); shaderTechTexLineAlwaysVisible->EndPass(); } @@ -479,13 +477,15 @@ // ---------------------------------------------------------------------------------------- // TODO: the shaders should probably be responsible for unbinding their textures - g_Renderer.BindTexture(1, 0); - g_Renderer.BindTexture(0, 0); + deviceCommandContext->BindTexture(1, GL_TEXTURE_2D, 0); + deviceCommandContext->BindTexture(0, GL_TEXTURE_2D, 0); CVertexBuffer::Unbind(); } -void OverlayRenderer::RenderTexturedOverlayLines(const CShaderProgramPtr& shader, bool alwaysVisible) +void OverlayRenderer::RenderTexturedOverlayLines( + Renderer::Backend::GL::CDeviceCommandContext* deviceCommandContext, + const CShaderProgramPtr& shader, bool alwaysVisible) { #if !CONFIG2_GLES if (g_Renderer.GetSceneRenderer().GetOverlayRenderMode() == WIREFRAME) @@ -500,7 +500,7 @@ continue; ENSURE(line->m_RenderData); - line->m_RenderData->Render(*line, shader); + line->m_RenderData->Render(deviceCommandContext, *line, shader); } #if !CONFIG2_GLES if (g_Renderer.GetSceneRenderer().GetOverlayRenderMode() == WIREFRAME) @@ -543,8 +543,6 @@ glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); #endif - glActiveTextureARB(GL_TEXTURE0); - CLOSTexture& los = g_Renderer.GetSceneRenderer().GetScene().GetLOSTexture(); shader->BindTexture(str_losTex, los.GetTexture()); @@ -569,8 +567,10 @@ const QuadBatchKey& maskPair = it->first; - shader->BindTexture(str_baseTex, maskPair.m_Texture); - shader->BindTexture(str_maskTex, maskPair.m_TextureMask); + maskPair.m_Texture->UploadBackendTextureIfNeeded(deviceCommandContext); + maskPair.m_TextureMask->UploadBackendTextureIfNeeded(deviceCommandContext); + shader->BindTexture(str_baseTex, maskPair.m_Texture->GetBackendTexture()); + shader->BindTexture(str_maskTex, maskPair.m_TextureMask->GetBackendTexture()); int streamflags = shader->GetStreamFlags(); @@ -596,8 +596,8 @@ shaderTech->EndPass(); // TODO: the shader should probably be responsible for unbinding its textures - g_Renderer.BindTexture(1, 0); - g_Renderer.BindTexture(0, 0); + deviceCommandContext->BindTexture(1, GL_TEXTURE_2D, 0); + deviceCommandContext->BindTexture(0, GL_TEXTURE_2D, 0); CVertexBuffer::Unbind(); @@ -621,8 +621,6 @@ if (g_Renderer.GetSceneRenderer().GetOverlayRenderMode() == WIREFRAME) glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); - glActiveTextureARB(GL_TEXTURE0); - CVector3D right = -viewCamera.GetOrientation().GetLeft(); CVector3D up = viewCamera.GetOrientation().GetUp(); @@ -652,7 +650,10 @@ { SOverlaySprite* sprite = m->sprites[i]; if (!i || sprite->m_Texture != m->sprites[i - 1]->m_Texture) - shader->BindTexture(str_baseTex, sprite->m_Texture); + { + sprite->m_Texture->UploadBackendTextureIfNeeded(deviceCommandContext); + shader->BindTexture(str_baseTex, sprite->m_Texture->GetBackendTexture()); + } shader->Uniform(str_colorMul, sprite->m_Color); Index: source/renderer/ParticleRenderer.cpp =================================================================== --- source/renderer/ParticleRenderer.cpp +++ source/renderer/ParticleRenderer.cpp @@ -157,7 +157,7 @@ shader->Uniform(str_transform, g_Renderer.GetSceneRenderer().GetViewCamera().GetViewProjection()); shader->Uniform(str_modelViewMatrix, g_Renderer.GetSceneRenderer().GetViewCamera().GetOrientation().GetInverse()); } - emitter->Bind(lastTech->GetShader()); + emitter->Bind(deviceCommandContext, lastTech->GetShader()); emitter->RenderArray(lastTech->GetShader()); } Index: source/renderer/PatchRData.cpp =================================================================== --- source/renderer/PatchRData.cpp +++ source/renderer/PatchRData.cpp @@ -757,11 +757,13 @@ TextureBatches& textureBatches = itTech->second; for (TextureBatches::iterator itt = textureBatches.begin(); itt != textureBatches.end(); ++itt) { - if (itt->first->GetMaterial().GetSamplers().size() != 0) + if (!itt->first->GetMaterial().GetSamplers().empty()) { const CMaterial::SamplersVector& samplers = itt->first->GetMaterial().GetSamplers(); for(const CMaterial::TextureSampler& samp : samplers) - shader->BindTexture(samp.Name, samp.Sampler); + samp.Sampler->UploadBackendTextureIfNeeded(deviceCommandContext); + for(const CMaterial::TextureSampler& samp : samplers) + shader->BindTexture(samp.Name, samp.Sampler->GetBackendTexture()); itt->first->GetMaterial().GetStaticUniforms().BindUniforms(shader); @@ -771,7 +773,7 @@ } else { - shader->BindTexture(str_baseTex, g_Renderer.GetTextureManager().GetErrorTexture()); + shader->BindTexture(str_baseTex, g_Renderer.GetTextureManager().GetErrorTexture()->GetBackendTexture()); } for (VertexBufferBatches::iterator itv = itt->second.begin(); itv != itt->second.end(); ++itv) @@ -974,7 +976,9 @@ { const CMaterial::SamplersVector& samplers = itt->m_Texture->GetMaterial().GetSamplers(); for (const CMaterial::TextureSampler& samp : samplers) - shader->BindTexture(samp.Name, samp.Sampler); + samp.Sampler->UploadBackendTextureIfNeeded(deviceCommandContext); + for (const CMaterial::TextureSampler& samp : samplers) + shader->BindTexture(samp.Name, samp.Sampler->GetBackendTexture()); Renderer::Backend::GL::CTexture* currentBlendTex = itt->m_Texture->m_TerrainAlpha->second.m_CompositeAlphaMap.get(); if (currentBlendTex != lastBlendTex) @@ -991,7 +995,7 @@ } else { - shader->BindTexture(str_baseTex, g_Renderer.GetTextureManager().GetErrorTexture()); + shader->BindTexture(str_baseTex, g_Renderer.GetTextureManager().GetErrorTexture()->GetBackendTexture()); } for (VertexBufferBatches::iterator itv = itt->m_Batches.begin(); itv != itt->m_Batches.end(); ++itv) Index: source/renderer/PostprocManager.cpp =================================================================== --- source/renderer/PostprocManager.cpp +++ source/renderer/PostprocManager.cpp @@ -124,8 +124,8 @@ Cleanup(); #define GEN_BUFFER_RGBA(name, w, h) \ - name = Renderer::Backend::GL::CTexture::Create2D( \ - Renderer::Backend::Format::R8G8B8A8, w, h, \ + name = g_VideoMode.GetBackendDevice()->CreateTexture2D( \ + "PostProc" #name, Renderer::Backend::Format::R8G8B8A8, w, h, \ Renderer::Backend::Sampler::MakeDefaultSampler( \ Renderer::Backend::Sampler::Filter::LINEAR, \ Renderer::Backend::Sampler::AddressMode::CLAMP_TO_EDGE)); @@ -154,15 +154,15 @@ #undef GEN_BUFFER_RGBA // Allocate the Depth/Stencil texture. - m_DepthTex = Renderer::Backend::GL::CTexture::Create2D( + m_DepthTex = g_VideoMode.GetBackendDevice()->CreateTexture2D("PostPRocDepthTexture", Renderer::Backend::Format::D24_S8, m_Width, m_Height, Renderer::Backend::Sampler::MakeDefaultSampler( Renderer::Backend::Sampler::Filter::LINEAR, Renderer::Backend::Sampler::AddressMode::CLAMP_TO_EDGE)); - glBindTexture(GL_TEXTURE_2D, m_DepthTex->GetHandle()); + g_Renderer.GetDeviceCommandContext()->BindTexture(0, GL_TEXTURE_2D, m_DepthTex->GetHandle()); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_NONE); - glBindTexture(GL_TEXTURE_2D, 0); + g_Renderer.GetDeviceCommandContext()->BindTexture(0, GL_TEXTURE_2D, 0); // Set up the framebuffers with some initial textures. m_CaptureFramebuffer = Renderer::Backend::GL::CFramebuffer::Create( @@ -605,7 +605,7 @@ { glEnable(GL_MULTISAMPLE); - m_MultisampleColorTex = Renderer::Backend::GL::CTexture::Create( + m_MultisampleColorTex = g_VideoMode.GetBackendDevice()->CreateTexture("PostProcColorMS", Renderer::Backend::GL::CTexture::Type::TEXTURE_2D_MULTISAMPLE, Renderer::Backend::Format::R8G8B8A8, m_Width, m_Height, Renderer::Backend::Sampler::MakeDefaultSampler( @@ -613,7 +613,7 @@ Renderer::Backend::Sampler::AddressMode::CLAMP_TO_EDGE), 1, m_MultisampleCount); // Allocate the Depth/Stencil texture. - m_MultisampleDepthTex = Renderer::Backend::GL::CTexture::Create( + m_MultisampleDepthTex = g_VideoMode.GetBackendDevice()->CreateTexture("PostProcDepthMS", Renderer::Backend::GL::CTexture::Type::TEXTURE_2D_MULTISAMPLE, Renderer::Backend::Format::D24_S8, m_Width, m_Height, Renderer::Backend::Sampler::MakeDefaultSampler( Index: source/renderer/Renderer.h =================================================================== --- source/renderer/Renderer.h +++ source/renderer/Renderer.h @@ -115,9 +115,6 @@ // get the last viewport SViewPort GetViewport(); - // bind a GL texture object to active unit - void BindTexture(int unit, unsigned int tex); - // return stats accumulated for current frame Stats& GetStats() { return m_Stats; } Index: source/renderer/Renderer.cpp =================================================================== --- source/renderer/Renderer.cpp +++ source/renderer/Renderer.cpp @@ -243,6 +243,8 @@ { NONCOPYABLE(Internals); public: + std::unique_ptr deviceCommandContext; + /// true if CRenderer::Open has been called bool IsOpen; @@ -270,10 +272,10 @@ CFontManager fontManager; - std::unique_ptr deviceCommandContext; - Internals() : - IsOpen(false), ShadersDirty(true), profileTable(g_Renderer.m_Stats), textureManager(g_VFS, false, false) + IsOpen(false), ShadersDirty(true), profileTable(g_Renderer.m_Stats), + deviceCommandContext(g_VideoMode.GetBackendDevice()->CreateCommandContext()), + textureManager(g_VFS, false, false) { } }; @@ -387,8 +389,6 @@ // Validate the currently selected render path SetRenderPath(g_RenderingOptions.GetRenderPath()); - m->deviceCommandContext = g_VideoMode.GetBackendDevice()->CreateCommandContext(); - if (m->postprocManager.IsEnabled()) m->postprocManager.Initialize(); @@ -484,6 +484,8 @@ g_TexMan.UploadResourcesIfNeeded(m->deviceCommandContext.get()); + m->textureManager.MakeUploadProgress(m->deviceCommandContext.get()); + // prepare before starting the renderer frame if (g_Game && g_Game->IsGameStarted()) g_Game->GetView()->BeginFrame(); @@ -756,8 +758,6 @@ PROFILE3("end frame"); m->sceneRenderer.EndFrame(); - - BindTexture(0, 0); } void CRenderer::SetViewport(const SViewPort &vp) @@ -771,13 +771,6 @@ return m_Viewport; } -void CRenderer::BindTexture(int unit, GLuint tex) -{ - glActiveTextureARB(GL_TEXTURE0+unit); - - glBindTexture(GL_TEXTURE_2D, tex); -} - void CRenderer::MakeShadersDirty() { m->ShadersDirty = true; Index: source/renderer/RenderingOptions.cpp =================================================================== --- source/renderer/RenderingOptions.cpp +++ source/renderer/RenderingOptions.cpp @@ -19,6 +19,7 @@ #include "RenderingOptions.h" +#include "graphics/TextureManager.h" #include "ps/CLogger.h" #include "ps/ConfigDB.h" #include "ps/CStr.h" @@ -221,6 +222,15 @@ }); m_ConfigHooks->Setup("renderactors", m_RenderActors); + + m_ConfigHooks->Setup("renderer.textures.quality", []() { + if (CRenderer::IsInitialised()) + g_Renderer.GetTextureManager().OnQualityChanged(); + }); + m_ConfigHooks->Setup("renderer.textures.maxanisotropy", []() { + if (CRenderer::IsInitialised()) + g_Renderer.GetTextureManager().OnQualityChanged(); + }); } void CRenderingOptions::ClearHooks() Index: source/renderer/SceneRenderer.cpp =================================================================== --- source/renderer/SceneRenderer.cpp +++ source/renderer/SceneRenderer.cpp @@ -368,7 +368,6 @@ glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); // setup some renderstate .. - glActiveTextureARB(GL_TEXTURE0); glLineWidth(2.0f); // render tiles edges Index: source/renderer/ShadowMap.cpp =================================================================== --- source/renderer/ShadowMap.cpp +++ source/renderer/ShadowMap.cpp @@ -527,14 +527,14 @@ if (g_RenderingOptions.GetShadowAlphaFix()) { - DummyTexture = Renderer::Backend::GL::CTexture::Create2D( + DummyTexture = g_VideoMode.GetBackendDevice()->CreateTexture2D("ShadowMapDummy", Renderer::Backend::Format::R8G8B8A8, Width, Height, Renderer::Backend::Sampler::MakeDefaultSampler( Renderer::Backend::Sampler::Filter::NEAREST, Renderer::Backend::Sampler::AddressMode::CLAMP_TO_EDGE)); } - Texture = Renderer::Backend::GL::CTexture::Create2D( + Texture = g_VideoMode.GetBackendDevice()->CreateTexture2D("ShadowMapDepth", backendFormat, Width, Height, Renderer::Backend::Sampler::MakeDefaultSampler( #if CONFIG2_GLES @@ -549,12 +549,12 @@ #if !CONFIG2_GLES - g_Renderer.BindTexture(0, Texture->GetHandle()); + g_Renderer.GetDeviceCommandContext()->BindTexture(0, GL_TEXTURE_2D, Texture->GetHandle()); // Enable automatic depth comparisons glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE, GL_INTENSITY); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL); - glBindTexture(GL_TEXTURE_2D, 0); + g_Renderer.GetDeviceCommandContext()->BindTexture(0, GL_TEXTURE_2D, 0); #endif ogl_WarnIfError(); @@ -579,7 +579,6 @@ { PROFILE("bind framebuffer"); - glBindTexture(GL_TEXTURE_2D, 0); deviceCommandContext->SetFramebuffer(m->Framebuffer.get()); } @@ -719,7 +718,7 @@ return; #if !CONFIG2_GLES - g_Renderer.BindTexture(0, m->Texture->GetHandle()); + deviceCommandContext->BindTexture(0, GL_TEXTURE_2D, m->Texture->GetHandle()); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_NONE); #endif @@ -756,7 +755,7 @@ texTech->EndPass(); #if !CONFIG2_GLES - g_Renderer.BindTexture(0, m->Texture->GetHandle()); + deviceCommandContext->BindTexture(0, GL_TEXTURE_2D, m->Texture->GetHandle()); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE); #endif Index: source/renderer/SkyManager.cpp =================================================================== --- source/renderer/SkyManager.cpp +++ source/renderer/SkyManager.cpp @@ -34,7 +34,9 @@ #include "ps/Filesystem.h" #include "ps/Game.h" #include "ps/Loader.h" +#include "ps/VideoMode.h" #include "ps/World.h" +#include "renderer/backend/gl/Device.h" #include "renderer/Renderer.h" #include "renderer/SceneRenderer.h" #include "renderer/RenderingOptions.h" @@ -111,7 +113,7 @@ } } - m_SkyCubeMap = Renderer::Backend::GL::CTexture::Create( + m_SkyCubeMap = g_VideoMode.GetBackendDevice()->CreateTexture("SkyCubeMap", Renderer::Backend::GL::CTexture::Type::TEXTURE_CUBE, Renderer::Backend::Format::R8G8B8A8, textures[0].m_Width, textures[0].m_Height, Renderer::Backend::Sampler::MakeDefaultSampler( Index: source/renderer/TerrainOverlay.cpp =================================================================== --- source/renderer/TerrainOverlay.cpp +++ source/renderer/TerrainOverlay.cpp @@ -30,6 +30,7 @@ #include "ps/Game.h" #include "ps/Profile.h" #include "ps/World.h" +#include "renderer/backend/gl/Device.h" #include "renderer/Renderer.h" #include "renderer/SceneRenderer.h" #include "renderer/TerrainRenderer.h" @@ -129,8 +130,6 @@ //glEnable(GL_POLYGON_OFFSET_LINE); glEnable(GL_POLYGON_OFFSET_FILL); - glActiveTextureARB(GL_TEXTURE0); - StartRender(); ssize_t min_i, min_j, max_i, max_j; @@ -338,12 +337,10 @@ const uint32_t requiredWidth = round_up_to_pow2(w); const uint32_t requiredHeight = round_up_to_pow2(h); - glActiveTextureARB(GL_TEXTURE0); - // Recreate the texture with new size if necessary if (!m_Texture || m_Texture->GetWidth() != requiredWidth || m_Texture->GetHeight() != requiredHeight) { - m_Texture = Renderer::Backend::GL::CTexture::Create2D( + m_Texture = deviceCommandContext->GetDevice()->CreateTexture2D("TerrainOverlayTexture", Renderer::Backend::Format::R8G8B8A8, requiredWidth, requiredHeight, Renderer::Backend::Sampler::MakeDefaultSampler( Renderer::Backend::Sampler::Filter::NEAREST, Index: source/renderer/TerrainRenderer.cpp =================================================================== --- source/renderer/TerrainRenderer.cpp +++ source/renderer/TerrainRenderer.cpp @@ -30,6 +30,7 @@ #include "graphics/ShaderManager.h" #include "graphics/TerritoryTexture.h" #include "graphics/TextRenderer.h" +#include "graphics/TextureManager.h" #include "maths/MathUtil.h" #include "ps/CLogger.h" #include "ps/CStrInternStatic.h" @@ -280,9 +281,9 @@ CDecalRData::RenderDecals(deviceCommandContext, visibleDecals, context, shadow); // restore OpenGL state - g_Renderer.BindTexture(1, 0); - g_Renderer.BindTexture(2, 0); - g_Renderer.BindTexture(3, 0); + deviceCommandContext->BindTexture(3, GL_TEXTURE_2D, 0); + deviceCommandContext->BindTexture(2, GL_TEXTURE_2D, 0); + deviceCommandContext->BindTexture(1, GL_TEXTURE_2D, 0); } @@ -417,8 +418,13 @@ const CCamera& camera = g_Renderer.GetSceneRenderer().GetViewCamera(); const double period = 8.0; - fancyWaterShader->BindTexture(str_normalMap, waterManager.m_NormalMap[waterManager.GetCurrentTextureIndex(period)]); - fancyWaterShader->BindTexture(str_normalMap2, waterManager.m_NormalMap[waterManager.GetNextTextureIndex(period)]); + // TODO: move uploading to a prepare function during loading. + const CTexturePtr& currentNormalTexture = waterManager.m_NormalMap[waterManager.GetCurrentTextureIndex(period)]; + const CTexturePtr& nextNormalTexture = waterManager.m_NormalMap[waterManager.GetNextTextureIndex(period)]; + currentNormalTexture->UploadBackendTextureIfNeeded(deviceCommandContext); + nextNormalTexture->UploadBackendTextureIfNeeded(deviceCommandContext); + fancyWaterShader->BindTexture(str_normalMap, currentNormalTexture->GetBackendTexture()); + fancyWaterShader->BindTexture(str_normalMap2, nextNormalTexture->GetBackendTexture()); if (waterManager.m_WaterFancyEffects) { @@ -533,7 +539,9 @@ waterSimpleTech->GetGraphicsPipelineStateDesc()); const CShaderProgramPtr& waterSimpleShader = waterSimpleTech->GetShader(); - waterSimpleShader->BindTexture(str_baseTex, waterManager.m_WaterTexture[waterManager.GetCurrentTextureIndex(1.6)]); + const CTexturePtr& waterTexture = waterManager.m_WaterTexture[waterManager.GetCurrentTextureIndex(1.6)]; + waterTexture->UploadBackendTextureIfNeeded(deviceCommandContext); + waterSimpleShader->BindTexture(str_baseTex, waterTexture->GetBackendTexture()); waterSimpleShader->BindTexture(str_losTex, losTexture.GetTextureSmooth()); waterSimpleShader->Uniform(str_transform, g_Renderer.GetSceneRenderer().GetViewCamera().GetViewProjection()); waterSimpleShader->Uniform(str_losTransform, losTexture.GetTextureMatrix()[0], losTexture.GetTextureMatrix()[12], 0.f, 0.f); @@ -547,9 +555,7 @@ data->RenderWaterSurface(waterSimpleShader, false); } - g_Renderer.BindTexture(1, 0); - - glActiveTextureARB(GL_TEXTURE0_ARB); + deviceCommandContext->BindTexture(1, GL_TEXTURE_2D, 0); waterSimpleTech->EndPass(); Index: source/renderer/TexturedLineRData.h =================================================================== --- source/renderer/TexturedLineRData.h +++ source/renderer/TexturedLineRData.h @@ -23,6 +23,7 @@ #include "graphics/ShaderProgramPtr.h" #include "graphics/TextureManager.h" #include "maths/BoundingBoxAligned.h" +#include "renderer/backend/gl/DeviceCommandContext.h" #include "renderer/VertexBufferManager.h" class CFrustum; @@ -51,7 +52,8 @@ ~CTexturedLineRData() = default; void Update(const SOverlayTexturedLine& line); - void Render(const SOverlayTexturedLine& line, const CShaderProgramPtr& shader); + void Render(Renderer::Backend::GL::CDeviceCommandContext* deviceCommandContext, + const SOverlayTexturedLine& line, const CShaderProgramPtr& shader); bool IsVisibleInFrustum(const CFrustum& frustum) const; Index: source/renderer/TexturedLineRData.cpp =================================================================== --- source/renderer/TexturedLineRData.cpp +++ source/renderer/TexturedLineRData.cpp @@ -35,7 +35,9 @@ * because it allows you to work with variable amounts of vertices and indices more easily. New code should prefer * to use VertexArray where possible, though. */ -void CTexturedLineRData::Render(const SOverlayTexturedLine& line, const CShaderProgramPtr& shader) +void CTexturedLineRData::Render( + Renderer::Backend::GL::CDeviceCommandContext* deviceCommandContext, + const SOverlayTexturedLine& line, const CShaderProgramPtr& shader) { if (!m_VB || !m_VBIndices) return; // might have failed to allocate @@ -44,8 +46,10 @@ const int streamFlags = shader->GetStreamFlags(); - shader->BindTexture(str_baseTex, line.m_TextureBase); - shader->BindTexture(str_maskTex, line.m_TextureMask); + line.m_TextureBase->UploadBackendTextureIfNeeded(deviceCommandContext); + line.m_TextureMask->UploadBackendTextureIfNeeded(deviceCommandContext); + shader->BindTexture(str_baseTex, line.m_TextureBase->GetBackendTexture()); + shader->BindTexture(str_maskTex, line.m_TextureMask->GetBackendTexture()); shader->Uniform(str_objectColor, line.m_Color); GLsizei stride = sizeof(CTexturedLineRData::SVertex); Index: source/renderer/WaterManager.cpp =================================================================== --- source/renderer/WaterManager.cpp +++ source/renderer/WaterManager.cpp @@ -145,7 +145,8 @@ { swprintf_s(pathname, ARRAY_SIZE(pathname), L"art/textures/animated/water/default/diffuse%02d.dds", (int)i+1); CTextureProperties textureProps(pathname); - textureProps.SetWrap(GL_REPEAT); + textureProps.SetAddressMode( + Renderer::Backend::Sampler::AddressMode::REPEAT); CTexturePtr texture = g_Renderer.GetTextureManager().CreateTexture(textureProps); texture->Prefetch(); @@ -168,7 +169,8 @@ // Load CoastalWaves { CTextureProperties textureProps(L"art/textures/terrain/types/water/coastalWave.png"); - textureProps.SetWrap(GL_REPEAT); + textureProps.SetAddressMode( + Renderer::Backend::Sampler::AddressMode::REPEAT); CTexturePtr texture = g_Renderer.GetTextureManager().CreateTexture(textureProps); texture->Prefetch(); m_WaveTex = texture; @@ -177,7 +179,8 @@ // Load Foam { CTextureProperties textureProps(L"art/textures/terrain/types/water/foam.png"); - textureProps.SetWrap(GL_REPEAT); + textureProps.SetAddressMode( + Renderer::Backend::Sampler::AddressMode::REPEAT); CTexturePtr texture = g_Renderer.GetTextureManager().CreateTexture(textureProps); texture->Prefetch(); m_FoamTex = texture; @@ -186,28 +189,30 @@ // Use screen-sized textures for minimum artifacts. m_RefTextureSize = round_up_to_pow2(g_Renderer.GetHeight()); + Renderer::Backend::GL::CDevice* backendDevice = g_VideoMode.GetBackendDevice(); + // Create reflection texture - m_ReflectionTexture = Renderer::Backend::GL::CTexture::Create2D( + m_ReflectionTexture = backendDevice->CreateTexture2D("WaterReflectionTexture", Renderer::Backend::Format::R8G8B8A8, m_RefTextureSize, m_RefTextureSize, Renderer::Backend::Sampler::MakeDefaultSampler( Renderer::Backend::Sampler::Filter::LINEAR, Renderer::Backend::Sampler::AddressMode::MIRRORED_REPEAT)); // Create refraction texture - m_RefractionTexture = Renderer::Backend::GL::CTexture::Create2D( + m_RefractionTexture = backendDevice->CreateTexture2D("WaterRefractionTexture", Renderer::Backend::Format::R8G8B8A8, m_RefTextureSize, m_RefTextureSize, Renderer::Backend::Sampler::MakeDefaultSampler( Renderer::Backend::Sampler::Filter::LINEAR, Renderer::Backend::Sampler::AddressMode::MIRRORED_REPEAT)); // Create depth textures - m_ReflFboDepthTexture = Renderer::Backend::GL::CTexture::Create2D( + m_ReflFboDepthTexture = backendDevice->CreateTexture2D("WaterReflectionDepthTexture", Renderer::Backend::Format::D32, m_RefTextureSize, m_RefTextureSize, Renderer::Backend::Sampler::MakeDefaultSampler( Renderer::Backend::Sampler::Filter::NEAREST, Renderer::Backend::Sampler::AddressMode::REPEAT)); - m_RefrFboDepthTexture = Renderer::Backend::GL::CTexture::Create2D( + m_RefrFboDepthTexture = backendDevice->CreateTexture2D("WaterRefractionDepthTexture", Renderer::Backend::Format::D32, m_RefTextureSize, m_RefTextureSize, Renderer::Backend::Sampler::MakeDefaultSampler( Renderer::Backend::Sampler::Filter::NEAREST, @@ -252,14 +257,16 @@ // Resize: Updates the fancy water textures. void WaterManager::Resize() { + Renderer::Backend::GL::CDevice* backendDevice = g_VideoMode.GetBackendDevice(); + // Create the Fancy Effects texture - m_FancyTexture = Renderer::Backend::GL::CTexture::Create2D( + m_FancyTexture = backendDevice->CreateTexture2D("WaterFancyTexture", Renderer::Backend::Format::R8G8B8A8, g_Renderer.GetWidth(), g_Renderer.GetHeight(), Renderer::Backend::Sampler::MakeDefaultSampler( Renderer::Backend::Sampler::Filter::LINEAR, Renderer::Backend::Sampler::AddressMode::REPEAT)); - m_FancyTextureDepth = Renderer::Backend::GL::CTexture::Create2D( + m_FancyTextureDepth = backendDevice->CreateTexture2D("WaterFancyDepthTexture", Renderer::Backend::Format::D32, g_Renderer.GetWidth(), g_Renderer.GetHeight(), Renderer::Backend::Sampler::MakeDefaultSampler( Renderer::Backend::Sampler::Filter::LINEAR, @@ -273,8 +280,9 @@ { swprintf_s(pathname, ARRAY_SIZE(pathname), L"art/textures/animated/water/%ls/normal00%02d.png", m_WaterType.c_str(), static_cast(i) + 1); CTextureProperties textureProps(pathname); - textureProps.SetWrap(GL_REPEAT); - textureProps.SetMaxAnisotropy(4); + textureProps.SetAddressMode( + Renderer::Backend::Sampler::AddressMode::REPEAT); + textureProps.SetAnisotropicFilter(true); CTexturePtr texture = g_Renderer.GetTextureManager().CreateTexture(textureProps); texture->Prefetch(); @@ -784,8 +792,10 @@ tech->GetGraphicsPipelineStateDesc()); const CShaderProgramPtr& shader = tech->GetShader(); - shader->BindTexture(str_waveTex, m_WaveTex); - shader->BindTexture(str_foamTex, m_FoamTex); + m_WaveTex->UploadBackendTextureIfNeeded(deviceCommandContext); + m_FoamTex->UploadBackendTextureIfNeeded(deviceCommandContext); + shader->BindTexture(str_waveTex, m_WaveTex->GetBackendTexture()); + shader->BindTexture(str_foamTex, m_FoamTex->GetBackendTexture()); shader->Uniform(str_time, (float)m_WaterTexTimer); shader->Uniform(str_transform, g_Renderer.GetSceneRenderer().GetViewCamera().GetViewProjection()); Index: source/renderer/backend/Format.h =================================================================== --- source/renderer/backend/Format.h +++ source/renderer/backend/Format.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 @@ -27,12 +27,21 @@ enum class Format { UNDEFINED, + R8G8B8, R8G8B8A8, + A8, + L8, + D16, D24, D24_S8, - D32 + D32, + + BC1_RGB, + BC1_RGBA, + BC2, + BC3 }; } // namespace Backend Index: source/renderer/backend/Sampler.cpp =================================================================== --- source/renderer/backend/Sampler.cpp +++ source/renderer/backend/Sampler.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 @@ -39,6 +39,7 @@ desc.addressModeW = addressMode; desc.anisotropyEnabled = false; desc.mipLODBias = 0.0f; + desc.borderColor = CColor(0.0f, 0.0f, 0.0f, 0.0f); return desc; } Index: source/renderer/backend/gl/Device.h =================================================================== --- source/renderer/backend/gl/Device.h +++ source/renderer/backend/gl/Device.h @@ -18,6 +18,7 @@ #ifndef INCLUDED_RENDERER_BACKEND_GL_DEVICE #define INCLUDED_RENDERER_BACKEND_GL_DEVICE +#include "renderer/backend/Format.h" #include "renderer/backend/gl/Framebuffer.h" #include "scriptinterface/ScriptForward.h" @@ -38,6 +39,7 @@ { class CDeviceCommandContext; +class CTexture; class CDevice { @@ -60,8 +62,20 @@ std::unique_ptr CreateCommandContext(); + CDeviceCommandContext* GetActiveCommandContext() { return m_ActiveCommandContext; } + + std::unique_ptr CreateTexture(const char* name, const CTexture::Type type, + const Format format, const uint32_t width, const uint32_t height, + const Sampler::Desc& defaultSamplerDesc, const uint32_t MIPLevelCount, const uint32_t sampleCount); + + std::unique_ptr CreateTexture2D(const char* name, + const Format format, const uint32_t width, const uint32_t height, + const Sampler::Desc& defaultSamplerDesc, const uint32_t MIPLevelCount = 1, const uint32_t sampleCount = 1); + void Present(); + bool IsFormatSupported(const Format format); + private: CDevice(); @@ -73,7 +87,14 @@ std::string m_DriverInformation; std::vector m_Extensions; + // GL can have the only one command context at once. + // TODO: remove as soon as we have no GL code outside backend, currently + // it's used only as a helper for transition. + CDeviceCommandContext* m_ActiveCommandContext = nullptr; + std::unique_ptr m_Backbuffer; + + bool m_HasS3TC = false; }; } // namespace GL Index: source/renderer/backend/gl/Device.cpp =================================================================== --- source/renderer/backend/gl/Device.cpp +++ source/renderer/backend/gl/Device.cpp @@ -25,6 +25,7 @@ #include "ps/ConfigDB.h" #include "ps/Profile.h" #include "renderer/backend/gl/DeviceCommandContext.h" +#include "renderer/backend/gl/Texture.h" #include "scriptinterface/JSON.h" #include "scriptinterface/Object.h" #include "scriptinterface/ScriptInterface.h" @@ -199,6 +200,16 @@ device->m_Backbuffer = CFramebuffer::CreateBackbuffer(); +#if CONFIG2_GLES + // Some GLES implementations have GL_EXT_texture_compression_dxt1 + // but that only supports DXT1 so we can't use it. + device->m_HasS3TC = ogl_HaveExtensions(0, "GL_EXT_texture_compression_s3tc", nullptr) == 0; +#else + // Note: we don't bother checking for GL_S3_s3tc - it is incompatible + // and irrelevant (was never widespread). + device->m_HasS3TC = ogl_HaveExtensions(0, "GL_ARB_texture_compression", "GL_EXT_texture_compression_s3tc", nullptr) == 0; +#endif + return device; } @@ -599,7 +610,25 @@ std::unique_ptr CDevice::CreateCommandContext() { - return CDeviceCommandContext::Create(this); + std::unique_ptr commandContet = CDeviceCommandContext::Create(this); + m_ActiveCommandContext = commandContet.get(); + return commandContet; +} + +std::unique_ptr CDevice::CreateTexture(const char* name, const CTexture::Type type, + const Format format, const uint32_t width, const uint32_t height, + const Sampler::Desc& defaultSamplerDesc, const uint32_t MIPLevelCount, const uint32_t sampleCount) +{ + return CTexture::Create(this, name, type, + format, width, height, defaultSamplerDesc, MIPLevelCount, sampleCount); +} + +std::unique_ptr CDevice::CreateTexture2D(const char* name, + const Format format, const uint32_t width, const uint32_t height, + const Sampler::Desc& defaultSamplerDesc, const uint32_t MIPLevelCount, const uint32_t sampleCount) +{ + return CreateTexture(name, CTexture::Type::TEXTURE_2D, + format, width, height, defaultSamplerDesc, MIPLevelCount, sampleCount); } void CDevice::Present() @@ -624,6 +653,42 @@ ONCE(LOGERROR("GL error %s (0x%04x) occurred", ogl_GetErrorName(err), err)); } +bool CDevice::IsFormatSupported(const Format format) +{ + bool supported = false; + switch (format) + { + case Format::UNDEFINED: + break; + + case Format::R8G8B8: FALLTHROUGH; + case Format::R8G8B8A8: FALLTHROUGH; + case Format::A8: FALLTHROUGH; + case Format::L8: + supported = true; + break; + + case Format::D16: FALLTHROUGH; + case Format::D24: FALLTHROUGH; + case Format::D32: + supported = true; + break; + case Format::D24_S8: +#if !CONFIG2_GLES + supported = true; +#endif + break; + + case Format::BC1_RGB: FALLTHROUGH; + case Format::BC1_RGBA: FALLTHROUGH; + case Format::BC2: FALLTHROUGH; + case Format::BC3: + supported = m_HasS3TC; + break; + } + return supported; +} + } // namespace GL } // namespace Backend Index: source/renderer/backend/gl/DeviceCommandContext.h =================================================================== --- source/renderer/backend/gl/DeviceCommandContext.h +++ source/renderer/backend/gl/DeviceCommandContext.h @@ -18,12 +18,15 @@ #ifndef INCLUDED_RENDERER_GL_DEVICECOMMANDCONTEXT #define INCLUDED_RENDERER_GL_DEVICECOMMANDCONTEXT +#include "lib/ogl.h" #include "renderer/backend/Format.h" #include "renderer/backend/PipelineState.h" #include +#include #include #include +#include namespace Renderer { @@ -70,6 +73,9 @@ }; void SetScissors(const uint32_t scissorCount, const ScissorRect* scissors); + // TODO: remove direct binding after moving shaders. + void BindTexture(const uint32_t unit, const GLenum target, const GLuint handle); + void Flush(); private: @@ -91,6 +97,21 @@ uint32_t m_ScissorCount = 0; // GL2.1 doesn't support more than 1 scissor. std::array m_Scissors; + + uint32_t m_ActiveTextureUnit = 0; + using BindUnit = std::pair; + std::array m_BindedTextures; + class ScopedBind + { + public: + ScopedBind(CDeviceCommandContext* deviceCommandContext, + const GLenum target, const GLuint handle); + + ~ScopedBind(); + private: + CDeviceCommandContext* m_DeviceCommandContext = nullptr; + BindUnit m_OldBindUnit; + }; }; } // namespace GL Index: source/renderer/backend/gl/DeviceCommandContext.cpp =================================================================== --- source/renderer/backend/gl/DeviceCommandContext.cpp +++ source/renderer/backend/gl/DeviceCommandContext.cpp @@ -100,6 +100,10 @@ CDeviceCommandContext::CDeviceCommandContext(CDevice* device) : m_Device(device) { + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, 0); + for (std::pair& unit : m_BindedTextures) + unit.first = unit.second = 0; } CDeviceCommandContext::~CDeviceCommandContext() = default; @@ -116,7 +120,10 @@ const uint32_t level, const uint32_t layer) { UploadTextureRegion(texture, format, data, dataSize, - 0, 0, texture->GetWidth(), texture->GetHeight(), level, layer); + 0, 0, + std::max(1u, texture->GetWidth() >> level), + std::max(1u, texture->GetHeight() >> level), + level, layer); } void CDeviceCommandContext::UploadTextureRegion( @@ -127,24 +134,73 @@ const uint32_t level, const uint32_t layer) { ENSURE(texture); + ENSURE(width > 0 && height > 0); if (texture->GetType() == CTexture::Type::TEXTURE_2D) { - ENSURE(level == 0 && layer == 0); - if (texture->GetFormat() == Format::R8G8B8A8 || texture->GetFormat() == Format::A8) + ENSURE(layer == 0); + if (texture->GetFormat() == Format::R8G8B8A8 || + texture->GetFormat() == Format::R8G8B8 || + texture->GetFormat() == Format::A8) { - ENSURE(width > 0 && height > 0); ENSURE(texture->GetFormat() == dataFormat); - const size_t bpp = dataFormat == Format::R8G8B8A8 ? 4 : 1; - ENSURE(dataSize == width * height * bpp); - ENSURE(xOffset + width <= texture->GetWidth()); - ENSURE(yOffset + height <= texture->GetHeight()); + size_t bytesPerPixel = 4; + GLenum pixelFormat = GL_RGBA; + switch (dataFormat) + { + case Format::R8G8B8A8: + break; + case Format::R8G8B8: + pixelFormat = GL_RGB; + bytesPerPixel = 3; + break; + case Format::A8: + pixelFormat = GL_ALPHA; + bytesPerPixel = 1; + break; + case Format::L8: + pixelFormat = GL_LUMINANCE; + bytesPerPixel = 1; + break; + default: + debug_warn("Unexpected format."); + break; + } + ENSURE(dataSize == width * height * bytesPerPixel); - glBindTexture(GL_TEXTURE_2D, texture->GetHandle()); + ScopedBind scopedBind(this, GL_TEXTURE_2D, texture->GetHandle()); glTexSubImage2D(GL_TEXTURE_2D, level, xOffset, yOffset, width, height, - dataFormat == Format::R8G8B8A8 ? GL_RGBA : GL_ALPHA, GL_UNSIGNED_BYTE, data); - glBindTexture(GL_TEXTURE_2D, 0); + pixelFormat, GL_UNSIGNED_BYTE, data); + ogl_WarnIfError(); + } + else if ( + texture->GetFormat() == Format::BC1_RGB || + texture->GetFormat() == Format::BC1_RGBA || + texture->GetFormat() == Format::BC2 || + texture->GetFormat() == Format::BC3) + { + ENSURE(xOffset == 0 && yOffset == 0); + ENSURE(texture->GetFormat() == dataFormat); + // TODO: add data size check. + GLenum internalFormat = GL_COMPRESSED_RGB_S3TC_DXT1_EXT; + switch (texture->GetFormat()) + { + case Format::BC1_RGBA: + internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; + break; + case Format::BC2: + internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; + break; + case Format::BC3: + internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; + break; + default: + break; + } + + ScopedBind scopedBind(this, GL_TEXTURE_2D, texture->GetHandle()); + glCompressedTexImage2DARB(GL_TEXTURE_2D, level, internalFormat, width, height, 0, dataSize, data); ogl_WarnIfError(); } else @@ -172,10 +228,8 @@ GL_TEXTURE_CUBE_MAP_NEGATIVE_Z }; - glBindTexture(GL_TEXTURE_CUBE_MAP, texture->GetHandle()); + ScopedBind scopedBind(this, GL_TEXTURE_CUBE_MAP, texture->GetHandle()); glTexImage2D(targets[layer], level, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); - glBindTexture(GL_TEXTURE_CUBE_MAP, 0); - ogl_WarnIfError(); } else @@ -185,9 +239,31 @@ debug_warn("Unsupported type"); } +void CDeviceCommandContext::BindTexture(const uint32_t unit, const GLenum target, const GLuint handle) +{ + ENSURE(unit < m_BindedTextures.size()); +#if CONFIG2_GLES + ENSURE(target == GL_TEXTURE_2D || target == GL_TEXTURE_CUBE_MAP); +#else + ENSURE(target == GL_TEXTURE_2D || target == GL_TEXTURE_CUBE_MAP || target == GL_TEXTURE_2D_MULTISAMPLE); +#endif + if (m_ActiveTextureUnit != unit) + { + glActiveTexture(GL_TEXTURE0 + unit); + m_ActiveTextureUnit = unit; + } + if (m_BindedTextures[unit].first != target) + glBindTexture(m_BindedTextures[unit].first, 0); + if (m_BindedTextures[unit].second != handle) + glBindTexture(target, handle); + m_BindedTextures[unit] = {target, handle}; +} + void CDeviceCommandContext::Flush() { ResetStates(); + + BindTexture(0, GL_TEXTURE_2D, 0); } void CDeviceCommandContext::ResetStates() @@ -472,6 +548,22 @@ m_ScissorCount = scissorCount; } +CDeviceCommandContext::ScopedBind::ScopedBind( + CDeviceCommandContext* deviceCommandContext, + const GLenum target, const GLuint handle) + : m_DeviceCommandContext(deviceCommandContext), + m_OldBindUnit(deviceCommandContext->m_BindedTextures[deviceCommandContext->m_ActiveTextureUnit]) +{ + m_DeviceCommandContext->BindTexture( + m_DeviceCommandContext->m_ActiveTextureUnit, target, handle); +} + +CDeviceCommandContext::ScopedBind::~ScopedBind() +{ + m_DeviceCommandContext->BindTexture( + m_DeviceCommandContext->m_ActiveTextureUnit, m_OldBindUnit.first, m_OldBindUnit.second); +} + } // namespace GL } // namespace Backend Index: source/renderer/backend/gl/Framebuffer.cpp =================================================================== --- source/renderer/backend/gl/Framebuffer.cpp +++ source/renderer/backend/gl/Framebuffer.cpp @@ -21,7 +21,6 @@ #include "lib/code_annotation.h" #include "lib/config2.h" -#include "lib/res/graphics/ogl_tex.h" #include "ps/CLogger.h" #include "ps/ConfigDB.h" #include "renderer/backend/gl/Device.h" Index: source/renderer/backend/gl/Texture.h =================================================================== --- source/renderer/backend/gl/Texture.h +++ source/renderer/backend/gl/Texture.h @@ -33,6 +33,8 @@ namespace GL { +class CDevice; + /** * Represents a low-level GL texture, encapsulates all properties initialization. */ @@ -48,17 +50,6 @@ ~CTexture(); - // GL before 3.3 doesn't support sampler objects, so each texture should have - // an own default sampler. - static std::unique_ptr Create(const Type type, const Format format, - const uint32_t width, const uint32_t height, - const Sampler::Desc& defaultSamplerDesc, const uint32_t MIPLevelCount, const uint32_t sampleCount); - - // Shorthands for particular types. - static std::unique_ptr Create2D(const Format format, - const uint32_t width, const uint32_t height, - const Sampler::Desc& defaultSamplerDesc, const uint32_t MIPLevelCount = 1, const uint32_t sampleCount = 1); - GLuint GetHandle() const { return m_Handle; } Type GetType() const { return m_Type; } @@ -68,8 +59,18 @@ uint32_t GetMIPLevelCount() const { return m_MIPLevelCount; } private: + friend class CDevice; + CTexture(); + CDevice* m_Device = nullptr; + + // GL before 3.3 doesn't support sampler objects, so each texture should have + // an own default sampler. + static std::unique_ptr Create(CDevice* device, const char* name, + const Type type, const Format format, const uint32_t width, const uint32_t height, + const Sampler::Desc& defaultSamplerDesc, const uint32_t MIPLevelCount, const uint32_t sampleCount); + GLuint m_Handle = 0; Type m_Type = Type::TEXTURE_2D; Index: source/renderer/backend/gl/Texture.cpp =================================================================== --- source/renderer/backend/gl/Texture.cpp +++ source/renderer/backend/gl/Texture.cpp @@ -21,8 +21,8 @@ #include "lib/code_annotation.h" #include "lib/config2.h" -#include "lib/res/graphics/ogl_tex.h" #include "renderer/backend/gl/Device.h" +#include "renderer/backend/gl/DeviceCommandContext.h" namespace Renderer { @@ -84,16 +84,8 @@ } // anonymous namespace // static -std::unique_ptr CTexture::Create2D(const Format format, - const uint32_t width, const uint32_t height, - const Sampler::Desc& defaultSamplerDesc, const uint32_t MIPLevelCount, const uint32_t sampleCount) -{ - return Create(Type::TEXTURE_2D, format, width, height, defaultSamplerDesc, MIPLevelCount, sampleCount); -} - -// static -std::unique_ptr CTexture::Create(const Type type, const Format format, - const uint32_t width, const uint32_t height, +std::unique_ptr CTexture::Create(CDevice* device, const char* name, + const Type type, const Format format, const uint32_t width, const uint32_t height, const Sampler::Desc& defaultSamplerDesc, const uint32_t MIPLevelCount, const uint32_t sampleCount) { std::unique_ptr texture(new CTexture()); @@ -102,6 +94,7 @@ ENSURE(width > 0 && height > 0 && MIPLevelCount > 0); ENSURE((type == Type::TEXTURE_2D_MULTISAMPLE && sampleCount > 1) || sampleCount == 1); + texture->m_Device = device; texture->m_Format = format; texture->m_Type = type; texture->m_Width = width; @@ -112,11 +105,9 @@ ogl_WarnIfError(); - glActiveTextureARB(GL_TEXTURE0); - const GLenum target = TypeToGLEnum(type); - glBindTexture(target, texture->m_Handle); + texture->m_Device->GetActiveCommandContext()->BindTexture(0, target, texture->m_Handle); // It's forbidden to set sampler state for multisample textures. if (type != Type::TEXTURE_2D_MULTISAMPLE) @@ -145,7 +136,8 @@ glTexParameteri(target, GL_TEXTURE_LOD_BIAS, defaultSamplerDesc.mipLODBias); #endif // !CONFIG2_GLES - if (type == Type::TEXTURE_2D && defaultSamplerDesc.anisotropyEnabled && ogl_tex_has_anisotropy()) + // TODO: ask anisotropy feature from Device. + if (type == Type::TEXTURE_2D && defaultSamplerDesc.anisotropyEnabled && ogl_HaveExtension("GL_EXT_texture_filter_anisotropic")) glTexParameterf(target, GL_TEXTURE_MAX_ANISOTROPY_EXT, defaultSamplerDesc.maxAnisotropy); if (defaultSamplerDesc.addressModeU == Sampler::AddressMode::CLAMP_TO_BORDER || @@ -157,10 +149,9 @@ ogl_WarnIfError(); - ENSURE(MIPLevelCount == 1); - if (type == CTexture::Type::TEXTURE_2D) { + bool compressedFormat = false; GLint internalFormat = GL_RGBA; // Actually pixel data is nullptr so it doesn't make sense to account // it, but in theory some buggy drivers might complain about invalid @@ -174,11 +165,21 @@ break; case Format::R8G8B8A8: break; + case Format::R8G8B8: + internalFormat = GL_RGB; + pixelFormat = GL_RGB; + pixelType = GL_UNSIGNED_BYTE; + break; case Format::A8: internalFormat = GL_ALPHA; pixelFormat = GL_ALPHA; pixelType = GL_UNSIGNED_BYTE; break; + case Format::L8: + internalFormat = GL_LUMINANCE; + pixelFormat = GL_LUMINANCE; + pixelType = GL_UNSIGNED_BYTE; + break; #if CONFIG2_GLES // GLES requires pixel type == UNSIGNED_SHORT or UNSIGNED_INT for depth. case Format::D16: FALLTHROUGH; @@ -213,11 +214,27 @@ pixelType = GL_UNSIGNED_INT_24_8_EXT; break; #endif + case Format::BC1_RGB: FALLTHROUGH; + case Format::BC1_RGBA: FALLTHROUGH; + case Format::BC2: FALLTHROUGH; + case Format::BC3: + compressedFormat = true; + break; + } + // glCompressedTexImage2D can't accept a null data, so we will initialize it during uploading. + if (!compressedFormat) + { + for (uint32_t level = 0; level < MIPLevelCount; ++level) + { + glTexImage2D(target, level, internalFormat, + std::max(1u, width >> level), std::max(1u, height >> level), + 0, pixelFormat, pixelType, nullptr); + } } - glTexImage2D(target, 0, internalFormat, width, height, 0, pixelFormat, pixelType, nullptr); } else if (type == CTexture::Type::TEXTURE_2D_MULTISAMPLE) { + ENSURE(MIPLevelCount == 1); #if !CONFIG2_GLES if (format == Format::R8G8B8A8) { @@ -236,7 +253,13 @@ ogl_WarnIfError(); - glBindTexture(target, 0); +#if KHR_DEBUG_ENABLED + glObjectLabel(GL_TEXTURE, texture->m_Handle, -1, name); +#else + UNUSED2(name); +#endif + + texture->m_Device->GetActiveCommandContext()->BindTexture(0, target, 0); return texture; } Index: source/simulation2/components/CCmpRallyPointRenderer.cpp =================================================================== --- source/simulation2/components/CCmpRallyPointRenderer.cpp +++ source/simulation2/components/CCmpRallyPointRenderer.cpp @@ -112,13 +112,17 @@ if (CRenderer::IsInitialised()) { CTextureProperties texturePropsBase(m_LineTexturePath); - texturePropsBase.SetWrap(GL_CLAMP_TO_BORDER, GL_CLAMP_TO_EDGE); - texturePropsBase.SetMaxAnisotropy(4.f); + texturePropsBase.SetAddressMode( + Renderer::Backend::Sampler::AddressMode::CLAMP_TO_BORDER, + Renderer::Backend::Sampler::AddressMode::CLAMP_TO_EDGE); + texturePropsBase.SetAnisotropicFilter(true); m_Texture = g_Renderer.GetTextureManager().CreateTexture(texturePropsBase); CTextureProperties texturePropsMask(m_LineTextureMaskPath); - texturePropsMask.SetWrap(GL_CLAMP_TO_BORDER, GL_CLAMP_TO_EDGE); - texturePropsMask.SetMaxAnisotropy(4.f); + texturePropsMask.SetAddressMode( + Renderer::Backend::Sampler::AddressMode::CLAMP_TO_BORDER, + Renderer::Backend::Sampler::AddressMode::CLAMP_TO_EDGE); + texturePropsMask.SetAnisotropicFilter(true); m_TextureMask = g_Renderer.GetTextureManager().CreateTexture(texturePropsMask); } } Index: source/simulation2/components/CCmpSelectable.cpp =================================================================== --- source/simulation2/components/CCmpSelectable.cpp +++ source/simulation2/components/CCmpSelectable.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 @@ -563,12 +563,16 @@ // Assuming we don't need the capability of swapping textures on-demand. CTextureProperties texturePropsBase(m_OverlayDescriptor.m_QuadTexture.c_str()); - texturePropsBase.SetWrap(GL_CLAMP_TO_BORDER, GL_CLAMP_TO_EDGE); - texturePropsBase.SetMaxAnisotropy(4.f); + texturePropsBase.SetAddressMode( + Renderer::Backend::Sampler::AddressMode::CLAMP_TO_BORDER, + Renderer::Backend::Sampler::AddressMode::CLAMP_TO_EDGE); + texturePropsBase.SetAnisotropicFilter(true); CTextureProperties texturePropsMask(m_OverlayDescriptor.m_QuadTextureMask.c_str()); - texturePropsMask.SetWrap(GL_CLAMP_TO_BORDER, GL_CLAMP_TO_EDGE); - texturePropsMask.SetMaxAnisotropy(4.f); + texturePropsMask.SetAddressMode( + Renderer::Backend::Sampler::AddressMode::CLAMP_TO_BORDER, + Renderer::Backend::Sampler::AddressMode::CLAMP_TO_EDGE); + texturePropsMask.SetAnisotropicFilter(true); m_UnitOverlay->m_Texture = g_Renderer.GetTextureManager().CreateTexture(texturePropsBase); m_UnitOverlay->m_TextureMask = g_Renderer.GetTextureManager().CreateTexture(texturePropsMask); Index: source/simulation2/components/CCmpTerritoryManager.cpp =================================================================== --- source/simulation2/components/CCmpTerritoryManager.cpp +++ source/simulation2/components/CCmpTerritoryManager.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 @@ -607,13 +607,17 @@ std::vector boundaries = ComputeBoundaries(); CTextureProperties texturePropsBase("art/textures/misc/territory_border.png"); - texturePropsBase.SetWrap(GL_CLAMP_TO_BORDER, GL_CLAMP_TO_EDGE); - texturePropsBase.SetMaxAnisotropy(2.f); + texturePropsBase.SetAddressMode( + Renderer::Backend::Sampler::AddressMode::CLAMP_TO_BORDER, + Renderer::Backend::Sampler::AddressMode::CLAMP_TO_EDGE); + texturePropsBase.SetAnisotropicFilter(true); CTexturePtr textureBase = g_Renderer.GetTextureManager().CreateTexture(texturePropsBase); CTextureProperties texturePropsMask("art/textures/misc/territory_border_mask.png"); - texturePropsMask.SetWrap(GL_CLAMP_TO_BORDER, GL_CLAMP_TO_EDGE); - texturePropsMask.SetMaxAnisotropy(2.f); + texturePropsMask.SetAddressMode( + Renderer::Backend::Sampler::AddressMode::CLAMP_TO_BORDER, + Renderer::Backend::Sampler::AddressMode::CLAMP_TO_EDGE); + texturePropsMask.SetAnisotropicFilter(true); CTexturePtr textureMask = g_Renderer.GetTextureManager().CreateTexture(texturePropsMask); CmpPtr cmpPlayerManager(GetSystemEntity()); Index: source/tools/atlas/GameInterface/Handlers/TerrainHandlers.cpp =================================================================== --- source/tools/atlas/GameInterface/Handlers/TerrainHandlers.cpp +++ source/tools/atlas/GameInterface/Handlers/TerrainHandlers.cpp @@ -91,11 +91,11 @@ if (canUsePreview) { size_t level = 0; - while ((texture.m_Width >> (level + 1)) >= width && (texture.m_Height >> (level + 1)) >= height) + while ((texture.m_Width >> (level + 1)) >= width && (texture.m_Height >> (level + 1)) >= height && level < texture.GetMIPLevels().size()) ++level; // Extract the middle section (as a representative preview), // and copy into buffer. - u8* data = texture.GetMipLevelData(level); + u8* data = texture.GetMIPLevels()[level].data; ENSURE(data); const size_t levelWidth = texture.m_Width >> level; const size_t levelHeight = texture.m_Height >> level;