1
0
forked from 0ad/0ad

Moves texture management to CDeviceCommandContext, adds quality options.

Comments By: nwtour, Stan
Tested By: nwtour, Stan
Differential Revision: https://code.wildfiregames.com/D4488
This was SVN commit r26365.
This commit is contained in:
Vladislav Belov 2022-02-13 19:30:28 +00:00
parent 451dc24a7e
commit 4de89c3db1
58 changed files with 1077 additions and 492 deletions

View File

@ -122,6 +122,12 @@ rendererbackend = "gl"
; 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

View File

@ -343,6 +343,30 @@
"tooltip": "Use actual water depth in rendering calculations.",
"dependencies": ["watereffects", "waterrefraction"],
"config": "waterrealdepth"
},
{
"type": "dropdown",
"label": "Texture 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": "Texture 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": "Disabled" },
{ "value": 2, "label": "2x", "tooltip": "2x" },
{ "value": 4, "label": "4x", "tooltip": "4x" },
{ "value": 8, "label": "8x", "tooltip": "8x" },
{ "value": 16, "label": "16x", "tooltip": "16x" }
]
}
]
},

View File

@ -38,11 +38,14 @@ namespace
// Array of 2D elements unrolled into 1D array.
using PlaneArray2D = std::array<float, 8>;
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 @@ inline void DrawTextureImpl(const CShaderProgramPtr& shader, CTexturePtr texture
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 @@ public:
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 @@ public:
Tech.reset();
}
Renderer::Backend::GL::CDeviceCommandContext* DeviceCommandContext;
CShaderTechniquePtr Tech;
};
@ -117,7 +126,7 @@ void CCanvas2D::DrawLine(const std::vector<CVector2D>& points, const float width
}
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 @@ void CCanvas2D::DrawRect(const CRect& rect, const CColor& color)
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 @@ void CCanvas2D::DrawTexture(
};
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 @@ void CCanvas2D::DrawText(CTextRenderer& textRenderer)
CShaderProgramPtr shader = m->Tech->GetShader();
shader->Uniform(str_grayscaleFactor, 0.0f);
textRenderer.Render(shader, GetDefaultGuiMatrix());
textRenderer.Render(m->DeviceCommandContext, shader, GetDefaultGuiMatrix());
}
void CCanvas2D::Flush()

View File

@ -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 @@ bool CFontManager::ReadFont(CFont* font, CStrIntern fontName)
// 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;

View File

@ -229,7 +229,7 @@ void CLOSTexture::ConstructTexture(Renderer::Backend::GL::CDeviceCommandContext*
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 @@ void CLOSTexture::ConstructTexture(Renderer::Backend::GL::CDeviceCommandContext*
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 @@ void CLOSTexture::ConstructTexture(Renderer::Backend::GL::CDeviceCommandContext*
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();

View File

@ -219,7 +219,7 @@ void CMiniMapTexture::CreateTextures(
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 @@ void CMiniMapTexture::CreateTextures(
m_TerrainData = std::make_unique<u32[]>((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(

View File

@ -84,7 +84,7 @@ bool CObjectEntry::BuildVariation(const std::vector<const std::set<CStr>*>& comp
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 @@ bool CObjectEntry::BuildVariation(const std::vector<const std::set<CStr>*>& comp
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.

View File

@ -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 @@ SOverlayTexturedLine::LineCapType SOverlayTexturedLine::StrToLineCapType(const s
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;

View File

@ -178,8 +178,12 @@ void CParticleEmitter::PrepareForRendering()
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 @@ void CParticleEmitter::Bind(const CShaderProgramPtr& shader)
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)

View File

@ -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 <map>
@ -120,7 +121,9 @@ public:
/**
* 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.

View File

@ -394,7 +394,8 @@ bool CParticleEmitterType::LoadXML(const VfsPath& path)
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)

View File

@ -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 <algorithm>
@ -171,8 +172,7 @@ public:
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 @@ public:
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 @@ public:
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 @@ public:
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 @@ int CShaderProgram::GetStreamFlags() const
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());

View File

@ -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 @@ public:
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);

View File

@ -125,12 +125,8 @@ CTerrainTextureEntry::CTerrainTextureEntry(CTerrainPropertiesPtr properties, con
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())
{

View File

@ -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 <algorithm>
@ -291,7 +293,7 @@ CTerrainTextureManager::LoadAlphaMap(const VfsPath& alphaMapType)
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 @@ void CTerrainTextureManager::UploadResourcesIfNeeded(
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();
}

View File

@ -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 @@ void CTerritoryTexture::ConstructTexture(Renderer::Backend::GL::CDeviceCommandCo
const uint32_t textureSize = round_up_to_pow2(static_cast<uint32_t>(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 @@ void CTerritoryTexture::ConstructTexture(Renderer::Backend::GL::CDeviceCommandCo
std::unique_ptr<u8[]> texData = std::make_unique<u8[]>(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();
{

View File

@ -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 @@ struct SBatchCompare
}
};
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<u16> indexes;
std::vector<t2f_v2i> vertexes;
@ -232,7 +235,8 @@ void CTextRenderer::Render(const CShaderProgramPtr& shader, const CMatrix3D& tra
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;

View File

@ -23,6 +23,7 @@
#include "maths/Rect.h"
#include "maths/Vector2D.h"
#include "ps/CStrIntern.h"
#include "renderer/backend/gl/DeviceCommandContext.h"
#include <list>
@ -102,7 +103,9 @@ public:
/**
* 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;

View File

@ -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,163 @@
#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 <algorithm>
#include <boost/filesystem.hpp>
#include <iomanip>
#include <set>
#include <sstream>
#include <unordered_map>
#include <unordered_set>
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<u8> 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;
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 << ")";
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::unique_ptr<Renderer::Backend::GL::CTexture> 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 +189,20 @@ private:
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 +213,23 @@ public:
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 +237,17 @@ public:
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 +258,9 @@ public:
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 +287,104 @@ public:
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<u8> fileData;
size_t fileSize;
texture->m_TextureData = std::make_unique<Tex>();
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));
texture->m_BaseColor = textureData.get_average_color();
// 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));
// 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));
}
}
ignore_result(ogl_tex_set_filter(h, filter));
// Upload to GL
if (!m_DisableGL && ogl_tex_upload(h, texture->m_Properties.m_Format) < 0)
else if (format == Renderer::Backend::Format::R8G8B8A8)
{
LOGERROR("Texture failed to upload: \"%s\"", texture->m_Properties.m_Path.string8());
ENSURE(textureData.m_Bpp == 32 && (textureData.m_Flags & TEX_ALPHA));
}
else
debug_warn("Unsupported format override.");
}
else
{
format = ChooseFormatAndTransformTextureDataIfNeeded(textureData, m_HasS3TC);
}
ogl_tex_free(h);
// Replace with error texture to make it obvious
texture->SetHandle(m_ErrorTexture.GetHandle());
if (format == Renderer::Backend::Format::UNDEFINED)
{
LOGERROR("Texture failed to choose format; \"%s\"", texture->m_Properties.m_Path.string8());
texture->ResetBackendTexture(
nullptr, m_ErrorTexture.GetTexture()->GetBackendTexture());
return;
}
// Let the texture object take ownership of this handle
texture->SetHandle(h, true);
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;
}
}
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;
}
}
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 +434,8 @@ public:
// 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 +508,8 @@ public:
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 +571,21 @@ public:
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 +647,14 @@ public:
if (files != m_HotloadFiles.end())
{
// Flag all textures using this file as needing reloading
for (std::set<std::weak_ptr<CTexture> >::iterator it = files->second.begin(); it != files->second.end(); ++it)
for (std::set<std::weak_ptr<CTexture>>::iterator it = files->second.begin(); it != files->second.end(); ++it)
{
if (std::shared_ptr<CTexture> 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 +662,17 @@ public:
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 +681,11 @@ public:
return size;
}
void OnQualityChanged()
{
ReloadAllTextures();
}
private:
PIVFS m_VFS;
CCacheLoader m_CacheLoader;
@ -526,6 +696,7 @@ private:
SingleColorTexture m_ErrorTexture;
SingleColorTexture m_WhiteTexture;
SingleColorTexture m_TransparentTexture;
bool m_SingleColorTexturesUploaded = false;
// Cache of all loaded textures
using TextureCache =
@ -536,44 +707,68 @@ private:
// 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<VfsPath, std::set<std::weak_ptr<CTexture>, std::owner_less<std::weak_ptr<CTexture> > > >;
std::unordered_map<VfsPath, std::set<std::weak_ptr<CTexture>, std::owner_less<std::weak_ptr<CTexture>>>>;
HotloadFilesMap m_HotloadFiles;
// Cache for the conversion settings files
using SettingsFilesMap =
std::unordered_map<VfsPath, std::shared_ptr<CTextureConverter::SettingsFile>>;
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<Renderer::Backend::GL::CTexture> 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()
{
if (m_Handle)
ogl_tex_free(m_Handle);
}
CTexture::~CTexture() = default;
void CTexture::Bind(size_t unit)
void CTexture::UploadBackendTextureIfNeeded(
Renderer::Backend::GL::CDeviceCommandContext* deviceCommandContext)
{
ogl_tex_bind(GetHandle(), unit);
}
Handle CTexture::GetHandle()
{
// 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.
if (IsUploaded())
return;
if (!IsLoaded())
TryLoad();
return m_Handle;
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;
}
Renderer::Backend::GL::CTexture* CTexture::GetBackendTexture()
{
return m_BackendTexture && IsUploaded() ? m_BackendTexture.get() : m_FallbackBackendTexture;
}
const Renderer::Backend::GL::CTexture* CTexture::GetBackendTexture() const
{
return m_BackendTexture && IsUploaded() ? m_BackendTexture.get() : m_FallbackBackendTexture;
}
bool CTexture::TryLoad()
@ -591,7 +786,7 @@ bool CTexture::TryLoad()
}
}
return (m_State == LOADED);
return IsLoaded();
}
void CTexture::Prefetch()
@ -605,42 +800,33 @@ void CTexture::Prefetch()
}
}
bool CTexture::IsLoaded()
void CTexture::ResetBackendTexture(
std::unique_ptr<Renderer::Backend::GL::CTexture> backendTexture,
Renderer::Backend::GL::CTexture* fallbackBackendTexture)
{
return (m_State == LOADED);
}
void CTexture::SetHandle(Handle handle, bool takeOwnership)
{
if (handle == m_Handle)
return;
if (!takeOwnership)
h_add_ref(handle);
ogl_tex_free(m_Handle);
m_Handle = handle;
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 +836,9 @@ u32 CTexture::GetBaseColor() const
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 +861,17 @@ bool CTextureManager::TextureExists(const VfsPath& path) const
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 +881,12 @@ bool CTextureManager::MakeProgress()
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 +896,8 @@ size_t CTextureManager::GetBytesUploaded() const
{
return m->GetBytesUploaded();
}
void CTextureManager::OnQualityChanged()
{
m->OnQualityChanged();
}

View File

@ -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 <memory>
@ -92,17 +93,17 @@ public:
* 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.
@ -112,6 +113,12 @@ public:
*/
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
@ -131,6 +138,11 @@ public:
*/
size_t GetBytesUploaded() const;
/**
* Should be called on any quality or anisotropic change.
*/
void OnQualityChanged();
private:
CTextureManagerImpl* m;
};
@ -150,38 +162,41 @@ public:
/**
* 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)
{
}
CTextureProperties(
const VfsPath& path, const Renderer::Backend::Format formatOverride)
: m_Path(path), m_FormatOverride(formatOverride)
{
}
/**
* Set min/mag filter mode (typically GL_LINEAR_MIPMAP_LINEAR, GL_NEAREST, etc).
* Set sampler address mode.
*/
void SetFilter(GLint filter) { m_Filter = filter; }
void SetAddressMode(const Renderer::Backend::Sampler::AddressMode addressMode)
{
m_AddressModeU = m_AddressModeV = addressMode;
}
/**
* Set wrapping mode (typically GL_REPEAT, GL_CLAMP_TO_EDGE, etc).
* Set sampler address mode separately for different coordinates.
*/
void SetWrap(GLint wrap) { m_WrapS = wrap; m_WrapT = wrap; }
void SetAddressMode(
const Renderer::Backend::Sampler::AddressMode addressModeU,
const Renderer::Backend::Sampler::AddressMode addressModeV)
{
m_AddressModeU = addressModeU;
m_AddressModeV = addressModeV;
}
/**
* Set wrapping mode (typically GL_REPEAT, GL_CLAMP_TO_EDGE, etc),
* separately for S and T.
* The value of max anisotropy is set by options. Though it might make sense
* to add an override.
*/
void SetWrap(GLint wrap_s, GLint wrap_t) { m_WrapS = wrap_s; m_WrapT = wrap_t; }
/**
* Set maximum anisotropy value. Must be >= 1.0. Should be a power of 2.
*/
void SetMaxAnisotropy(float aniso) { m_Aniso = aniso; }
/**
* Set GL texture upload format, to override the default.
* Typically GL_ALPHA or GL_LUMINANCE for 8-bit textures.
*/
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 @@ public:
//
// 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 @@ private:
*/
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 @@ public:
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 { return m_State == LOADED; }
/**
* Returns whether the texture data is currently uploaded.
*/
bool IsUploaded() const { return m_State == UPLOADED; }
/**
* Activate the prefetching optimisation for this texture.
@ -294,25 +312,42 @@ public:
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<Renderer::Backend::GL::CTexture> texture,
Renderer::Backend::GL::CTexture* fallback,
const CTextureProperties& props, CTextureManagerImpl* textureManager);
void ResetBackendTexture(
std::unique_ptr<Renderer::Backend::GL::CTexture> backendTexture,
Renderer::Backend::GL::CTexture* fallbackBackendTexture);
const CTextureProperties m_Properties;
Handle m_Handle;
std::unique_ptr<Renderer::Backend::GL::CTexture> m_BackendTexture;
// It's possible to m_FallbackBackendTexture references m_BackendTexture.
Renderer::Backend::GL::CTexture* m_FallbackBackendTexture = nullptr;
u32 m_BaseColor;
std::unique_ptr<Tex> 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;

View File

@ -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 @@ void CGUI::Xeromyces_ReadImage(const XMBData& xmb, XMBElement element, CGUISprit
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));
}

View File

@ -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 <map>
#include <memory>
@ -55,7 +55,7 @@ public:
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 @@ public:
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<SGUIImageEffects> m_Effects;

View File

@ -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 @@ void GUIRenderer::UpdateDrawCallCache(const CGUI& pGUI, DrawCalls& Calls, const
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 @@ void GUIRenderer::Draw(DrawCalls& Calls, CCanvas2D& canvas)
// 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());

View File

@ -220,7 +220,8 @@ void CMiniMap::RecreateFlareTextures()
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));
}
}

View File

@ -474,12 +474,14 @@ Status Tex::transform(size_t transforms)
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 @@ Status Tex::wrap(size_t w, size_t h, size_t bpp, size_t flags, const std::shared
m_Ofs = ofs;
CHECK_TEX(this);
UpdateMIPLevels();
return INFO::OK;
}
@ -632,31 +637,6 @@ u8* Tex::get_data()
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<u32>(levelWidth / 2, 1);
levelHeight = std::max<u32>(levelHeight / 2, 1);
}
return nullptr;
}
// returns color of 1x1 mipmap level
u32 Tex::get_average_color() const
{
@ -765,6 +745,8 @@ Status Tex::decode(const std::shared_ptr<u8>& Data, size_t DataSize)
CHECK_TEX(this);
UpdateMIPLevels();
return INFO::OK;
}
@ -795,3 +777,39 @@ Status Tex::encode(const OsPath& extension, DynArray* da)
return INFO::OK;
}
void Tex::UpdateMIPLevels()
{
m_MIPLevels.clear();
if (m_Flags & TEX_MIPMAPS)
{
// We add one because we need to account the smallest 1x1 level.
m_MIPLevels.reserve(ceil_log2(std::max(m_Width, m_Height)) + 1);
}
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<u32>(levelWidth / 2, 1);
levelHeight = std::max<u32>(levelHeight / 2, 1);
}
}

View File

@ -109,6 +109,7 @@ library and IO layer. Read and write are zero-copy.
#include "lib/file/vfs/vfs_path.h"
#include "lib/allocators/dynarray.h"
#include <vector>
namespace ERR
{
@ -202,12 +203,21 @@ enum TexFlags
};
/**
* 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 @@ struct Tex
**/
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<MIPLevel>& GetMIPLevels() const { return m_MIPLevels; }
/**
* return the ARGB value of the 1x1 mipmap level of the texture.
@ -356,6 +360,10 @@ struct Tex
**/
size_t img_size() const;
private:
void UpdateMIPLevels();
std::vector<MIPLevel> m_MIPLevels;
};

View File

@ -21,7 +21,7 @@
#include "lib/os_path.h"
#include "lib/file/vfs/vfs_path.h"
struct Tex;
class Tex;
void WriteSystemInfo();

View File

@ -177,7 +177,9 @@ void CDecalRData::RenderDecals(
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);

View File

@ -693,7 +693,8 @@ void ShaderModelRenderer::Render(
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 @@ void ShaderModelRenderer::Render(
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)
{

View File

@ -409,8 +409,6 @@ void OverlayRenderer::RenderTexturedOverlayLines(Renderer::Backend::GL::CDeviceC
ogl_WarnIfError();
glActiveTextureARB(GL_TEXTURE0);
CLOSTexture& los = g_Renderer.GetSceneRenderer().GetScene().GetLOSTexture();
// ----------------------------------------------------------------------------------------
@ -439,7 +437,7 @@ void OverlayRenderer::RenderTexturedOverlayLines(Renderer::Backend::GL::CDeviceC
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 @@ void OverlayRenderer::RenderTexturedOverlayLines(Renderer::Backend::GL::CDeviceC
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 @@ void OverlayRenderer::RenderTexturedOverlayLines(Renderer::Backend::GL::CDeviceC
// ----------------------------------------------------------------------------------------
// 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 @@ void OverlayRenderer::RenderTexturedOverlayLines(const CShaderProgramPtr& shader
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 @@ void OverlayRenderer::RenderQuadOverlays(
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 @@ void OverlayRenderer::RenderQuadOverlays(
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 @@ void OverlayRenderer::RenderQuadOverlays(
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 @@ void OverlayRenderer::RenderForegroundOverlays(
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 @@ void OverlayRenderer::RenderForegroundOverlays(
{
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);

View File

@ -107,7 +107,8 @@ public:
* (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 @@ private:
* 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.

View File

@ -157,7 +157,7 @@ void ParticleRenderer::RenderParticles(
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());
}

View File

@ -757,11 +757,13 @@ void CPatchRData::RenderBases(
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 @@ void CPatchRData::RenderBases(
}
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 @@ void CPatchRData::RenderBlends(
{
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 @@ void CPatchRData::RenderBlends(
}
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)

View File

@ -124,8 +124,8 @@ void CPostprocManager::RecreateBuffers()
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 @@ void CPostprocManager::RecreateBuffers()
#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 @@ void CPostprocManager::CreateMultisampleBuffer()
{
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 @@ void CPostprocManager::CreateMultisampleBuffer()
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(

View File

@ -243,6 +243,8 @@ class CRenderer::Internals
{
NONCOPYABLE(Internals);
public:
std::unique_ptr<Renderer::Backend::GL::CDeviceCommandContext> deviceCommandContext;
/// true if CRenderer::Open has been called
bool IsOpen;
@ -270,10 +272,10 @@ public:
CFontManager fontManager;
std::unique_ptr<Renderer::Backend::GL::CDeviceCommandContext> 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 @@ bool CRenderer::Open(int width, int height)
// 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 @@ void CRenderer::RenderFrameImpl(const bool renderGUI, const bool renderLogger)
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 @@ void CRenderer::EndFrame()
PROFILE3("end frame");
m->sceneRenderer.EndFrame();
BindTexture(0, 0);
}
void CRenderer::SetViewport(const SViewPort &vp)
@ -771,13 +771,6 @@ SViewPort CRenderer::GetViewport()
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;

View File

@ -115,9 +115,6 @@ public:
// 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; }

View File

@ -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 @@ void CRenderingOptions::ReadConfigAndSetupHooks()
});
m_ConfigHooks->Setup("renderactors", m_RenderActors);
m_ConfigHooks->Setup("textures.quality", []() {
if (CRenderer::IsInitialised())
g_Renderer.GetTextureManager().OnQualityChanged();
});
m_ConfigHooks->Setup("textures.maxanisotropy", []() {
if (CRenderer::IsInitialised())
g_Renderer.GetTextureManager().OnQualityChanged();
});
}
void CRenderingOptions::ClearHooks()

View File

@ -368,7 +368,6 @@ void CSceneRenderer::RenderPatches(
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
// setup some renderstate ..
glActiveTextureARB(GL_TEXTURE0);
glLineWidth(2.0f);
// render tiles edges

View File

@ -527,14 +527,14 @@ void ShadowMapInternals::CreateTexture()
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 @@ void ShadowMapInternals::CreateTexture()
#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 @@ void ShadowMap::BeginRender()
{
PROFILE("bind framebuffer");
glBindTexture(GL_TEXTURE_2D, 0);
deviceCommandContext->SetFramebuffer(m->Framebuffer.get());
}
@ -719,7 +718,7 @@ void ShadowMap::RenderDebugTexture(
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 @@ void ShadowMap::RenderDebugTexture(
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

View File

@ -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 @@ void SkyManager::LoadAndUploadSkyTexturesIfNeeded(
}
}
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(

View File

@ -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 @@ void TerrainOverlay::RenderBeforeWater(
//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 @@ void TerrainTextureOverlay::RenderAfterWater(
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,

View File

@ -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 @@ void TerrainRenderer::RenderTerrainShader(
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 @@ bool TerrainRenderer::RenderFancyWater(
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 @@ void TerrainRenderer::RenderSimpleWater(
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 @@ void TerrainRenderer::RenderSimpleWater(
data->RenderWaterSurface(waterSimpleShader, false);
}
g_Renderer.BindTexture(1, 0);
glActiveTextureARB(GL_TEXTURE0_ARB);
deviceCommandContext->BindTexture(1, GL_TEXTURE_2D, 0);
waterSimpleTech->EndPass();

View File

@ -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 @@ void CTexturedLineRData::Render(const SOverlayTexturedLine& line, const CShaderP
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);

View File

@ -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 @@ public:
~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;

View File

@ -145,7 +145,8 @@ int WaterManager::LoadWaterTextures()
{
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 @@ int WaterManager::LoadWaterTextures()
// 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 @@ int WaterManager::LoadWaterTextures()
// 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 @@ int WaterManager::LoadWaterTextures()
// 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 @@ int WaterManager::LoadWaterTextures()
// 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 @@ void WaterManager::ReloadWaterNormalTextures()
{
swprintf_s(pathname, ARRAY_SIZE(pathname), L"art/textures/animated/water/%ls/normal00%02d.png", m_WaterType.c_str(), static_cast<int>(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 @@ void WaterManager::RenderWaves(
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());

View File

@ -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 @@ namespace Backend
enum class Format
{
UNDEFINED,
R8G8B8,
R8G8B8A8,
A8,
L8,
D16,
D24,
D24_S8,
D32
D32,
BC1_RGB,
BC1_RGBA,
BC2,
BC3
};
} // namespace Backend

View File

@ -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 MakeDefaultSampler(Filter filter, AddressMode addressMode)
desc.addressModeW = addressMode;
desc.anisotropyEnabled = false;
desc.mipLODBias = 0.0f;
desc.borderColor = CColor(0.0f, 0.0f, 0.0f, 0.0f);
return desc;
}

View File

@ -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 @@ std::unique_ptr<CDevice> CDevice::Create(SDL_Window* window, const bool arb)
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 @@ void CDevice::Report(const ScriptRequest& rq, JS::HandleValue settings)
std::unique_ptr<CDeviceCommandContext> CDevice::CreateCommandContext()
{
return CDeviceCommandContext::Create(this);
std::unique_ptr<CDeviceCommandContext> commandContet = CDeviceCommandContext::Create(this);
m_ActiveCommandContext = commandContet.get();
return commandContet;
}
std::unique_ptr<CTexture> 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<CTexture> 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 @@ void CDevice::Present()
ONCE(LOGERROR("GL error %s (0x%04x) occurred", ogl_GetErrorName(err), err));
}
bool CDevice::IsFormatSupported(const Format format) const
{
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

View File

@ -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 @@ namespace GL
{
class CDeviceCommandContext;
class CTexture;
class CDevice
{
@ -60,8 +62,20 @@ public:
std::unique_ptr<CDeviceCommandContext> CreateCommandContext();
CDeviceCommandContext* GetActiveCommandContext() { return m_ActiveCommandContext; }
std::unique_ptr<CTexture> 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<CTexture> 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) const;
private:
CDevice();
@ -73,7 +87,14 @@ private:
std::string m_DriverInformation;
std::vector<std::string> 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<CFramebuffer> m_Backbuffer;
bool m_HasS3TC = false;
};
} // namespace GL

View File

@ -24,6 +24,7 @@
#include "renderer/backend/gl/Mapping.h"
#include "renderer/backend/gl/Texture.h"
#include <algorithm>
#include <limits>
namespace Renderer
@ -100,6 +101,10 @@ std::unique_ptr<CDeviceCommandContext> CDeviceCommandContext::Create(CDevice* de
CDeviceCommandContext::CDeviceCommandContext(CDevice* device)
: m_Device(device)
{
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, 0);
for (std::pair<GLenum, GLuint>& unit : m_BoundTextures)
unit.first = unit.second = 0;
}
CDeviceCommandContext::~CDeviceCommandContext() = default;
@ -116,7 +121,10 @@ void CDeviceCommandContext::UploadTexture(
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 +135,73 @@ void CDeviceCommandContext::UploadTextureRegion(
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 +229,8 @@ void CDeviceCommandContext::UploadTextureRegion(
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 +240,33 @@ void CDeviceCommandContext::UploadTextureRegion(
debug_warn("Unsupported type");
}
void CDeviceCommandContext::BindTexture(const uint32_t unit, const GLenum target, const GLuint handle)
{
ENSURE(unit < m_BoundTextures.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_BoundTextures[unit].first == target && m_BoundTextures[unit].second == handle)
return;
if (m_ActiveTextureUnit != unit)
{
glActiveTexture(GL_TEXTURE0 + unit);
m_ActiveTextureUnit = unit;
}
if (m_BoundTextures[unit].first != target && m_BoundTextures[unit].first && m_BoundTextures[unit].second)
glBindTexture(m_BoundTextures[unit].first, 0);
if (m_BoundTextures[unit].second != handle)
glBindTexture(target, handle);
m_BoundTextures[unit] = {target, handle};
}
void CDeviceCommandContext::Flush()
{
ResetStates();
BindTexture(0, GL_TEXTURE_2D, 0);
}
void CDeviceCommandContext::ResetStates()
@ -472,6 +551,22 @@ void CDeviceCommandContext::SetScissors(const uint32_t scissorCount, const Sciss
m_ScissorCount = scissorCount;
}
CDeviceCommandContext::ScopedBind::ScopedBind(
CDeviceCommandContext* deviceCommandContext,
const GLenum target, const GLuint handle)
: m_DeviceCommandContext(deviceCommandContext),
m_OldBindUnit(deviceCommandContext->m_BoundTextures[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

View File

@ -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 <array>
#include <cstdint>
#include <memory>
#include <optional>
#include <utility>
namespace Renderer
{
@ -70,6 +73,9 @@ public:
};
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 @@ private:
uint32_t m_ScissorCount = 0;
// GL2.1 doesn't support more than 1 scissor.
std::array<ScissorRect, 1> m_Scissors;
uint32_t m_ActiveTextureUnit = 0;
using BindUnit = std::pair<GLenum, GLuint>;
std::array<BindUnit, 16> m_BoundTextures;
class ScopedBind
{
public:
ScopedBind(CDeviceCommandContext* deviceCommandContext,
const GLenum target, const GLuint handle);
~ScopedBind();
private:
CDeviceCommandContext* m_DeviceCommandContext = nullptr;
BindUnit m_OldBindUnit;
};
};
} // namespace GL

View File

@ -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"

View File

@ -21,8 +21,10 @@
#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"
#include <algorithm>
namespace Renderer
{
@ -84,16 +86,8 @@ GLenum TypeToGLEnum(CTexture::Type type)
} // anonymous namespace
// static
std::unique_ptr<CTexture> 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> CTexture::Create(const Type type, const Format format,
const uint32_t width, const uint32_t height,
std::unique_ptr<CTexture> 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<CTexture> texture(new CTexture());
@ -102,6 +96,7 @@ std::unique_ptr<CTexture> CTexture::Create(const Type type, const Format format,
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 +107,9 @@ std::unique_ptr<CTexture> CTexture::Create(const Type type, const Format format,
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 +138,8 @@ std::unique_ptr<CTexture> CTexture::Create(const Type type, const Format format,
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 +151,9 @@ std::unique_ptr<CTexture> CTexture::Create(const Type type, const Format format,
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 +167,21 @@ std::unique_ptr<CTexture> CTexture::Create(const Type type, const Format format,
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 +216,27 @@ std::unique_ptr<CTexture> CTexture::Create(const Type type, const Format format,
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 +255,13 @@ std::unique_ptr<CTexture> CTexture::Create(const Type type, const Format format,
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;
}

View File

@ -23,6 +23,7 @@
#include "renderer/backend/Sampler.h"
#include <cstdint>
#include <memory>
namespace Renderer
{
@ -33,6 +34,8 @@ namespace Backend
namespace GL
{
class CDevice;
/**
* Represents a low-level GL texture, encapsulates all properties initialization.
*/
@ -48,17 +51,6 @@ public:
~CTexture();
// GL before 3.3 doesn't support sampler objects, so each texture should have
// an own default sampler.
static std::unique_ptr<CTexture> 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<CTexture> 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 +60,18 @@ public:
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<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);
GLuint m_Handle = 0;
Type m_Type = Type::TEXTURE_2D;

View File

@ -112,13 +112,17 @@ void CCmpRallyPointRenderer::Init(const CParamNode& paramNode)
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);
}
}

View File

@ -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 @@ void CCmpSelectable::UpdateDynamicOverlay(float frameOffset)
// 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);

View File

@ -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 @@ void CCmpTerritoryManager::UpdateBoundaryLines()
std::vector<STerritoryBoundary> 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<ICmpPlayerManager> cmpPlayerManager(GetSystemEntity());

View File

@ -91,11 +91,11 @@ sTerrainTexturePreview GetPreview(CTerrainTextureEntry* tex, size_t width, size_
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;