Fixes A8 framebuffer format used for LOS interpolation and enables smooth LOS by default.

Tested By: Langbart, mastoras
Differential Revision: https://code.wildfiregames.com/D4654
This was SVN commit r26906.
This commit is contained in:
Vladislav Belov 2022-05-26 16:36:57 +00:00
parent 68d73e1726
commit 095838da0a
29 changed files with 185 additions and 51 deletions

View File

@ -139,7 +139,7 @@ textures.maxanisotropy = 2
gpuskinning = false
; Use smooth LOS interpolation
smoothlos = false
smoothlos = true
; Use screen-space postprocessing filters (HDR, bloom, DOF, etc). Incompatible with fixed renderpath.
postproc = false

View File

@ -12,6 +12,6 @@ TEX los2_tex, v_los, texture[1], 2D;
TEMP smoothing;
MOV_SAT smoothing, delta.x;
LRP result.color.a, smoothing, los1_tex.a, los2_tex.a;
LRP result.color, smoothing, los1_tex.rrrr, los2_tex.rrrr;
END

View File

@ -21,12 +21,11 @@
TEMP tex;
TEX tex, fragment.texcoord[0], texture[0], 2D;
SUB tex.a, 1.0, tex.a;
MOV result.color.r, 0.0;
MOV result.color.g, 0.0;
MOV result.color.b, 0.0;
MOV result.color.a, tex.a;
MOV result.color.r, tex.r;
MOV result.color.g, tex.r;
MOV result.color.b, tex.r;
MOV result.color.a, tex.r;
#endif
#if MINIMAP_POINT

View File

@ -148,8 +148,8 @@ TEX tex, v_tex, texture[0], 2D;
#if !IGNORE_LOS
// Multiply everything by the LOS texture
TEX tex.a, v_los, texture[2], 2D;
MUL color.rgb, color, tex.a;
TEX tex.r, v_los, texture[2], 2D;
MUL color.rgb, color, tex.r;
#endif
MUL result.color.rgb, color, shadingColor;

View File

@ -16,10 +16,10 @@ TEX mask, fragment.texcoord[0], texture[1], 2D;
#if IGNORE_LOS
MOV result.color.rgb, color;
#else
// Multiply RGB by LOS texture (alpha channel)
// Multiply RGB by LOS texture (red channel)
TEMP los;
TEX los, fragment.texcoord[1], texture[2], 2D;
MUL result.color.rgb, color, los.a;
MUL result.color.rgb, color, los.r;
#endif
// Use alpha from base texture, combined with the object color/fragment alpha.

View File

@ -17,7 +17,7 @@ MUL color.rgb, color, tex;
// Multiply everything by the LOS texture
TEX losTex, v_los, texture[1], 2D;
MUL result.color.rgb, color, losTex.a;
MUL result.color.rgb, color, losTex.r;
MUL result.color.a, tex, fragment.color;
END

View File

@ -87,8 +87,8 @@ TEX color, fragment.texcoord[0], texture[0], 2D;
#endif
// Multiply everything by the LOS texture
TEX tex.a, fragment.texcoord[3], texture[3], 2D;
MUL color.rgb, color, tex.a;
TEX tex.r, fragment.texcoord[3], texture[3], 2D;
MUL color.rgb, color, tex.r;
#if DECAL
MUL result.color.rgb, color, shadingColor;

View File

@ -11,7 +11,7 @@ MUL diffuse, diffuse, color;
TEMP los;
TEX los, v_losCoords, texture[1], 2D;
MUL diffuse, diffuse, los.a;
MUL diffuse, diffuse, los.r;
MOV result.color, diffuse;
END

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<effect>
<technique>
<require shaders="arb"/>
<define name="MINIMAP_LOS" value="1"/>
<pass shader="arb/minimap">
<depth test="FALSE" mask="false"/>
<color mask_red="FALSE" mask_green="FALSE" mask_blue="FALSE" mask_alpha="TRUE"/>
</pass>
</technique>
<technique>
<require shaders="glsl"/>
<define name="MINIMAP_LOS" value="1"/>
<pass shader="glsl/minimap">
<depth test="FALSE" mask="false"/>
<color mask_red="FALSE" mask_green="FALSE" mask_blue="FALSE" mask_alpha="TRUE"/>
</pass>
</technique>
</effect>

View File

@ -10,7 +10,7 @@
float getLOS()
{
#if !IGNORE_LOS
float los = texture2D(losTex, v_los).a;
float los = texture2D(losTex, v_los).r;
float threshold = 0.03;
return (los - threshold) / (1.0 - threshold);
#else

View File

@ -9,9 +9,9 @@ uniform float delta;
void main(void)
{
float los2 = texture2D(losTex1, v_tex).a;
float los1 = texture2D(losTex2, v_tex).a;
vec4 los2 = texture2D(losTex1, v_tex).rrrr;
vec4 los1 = texture2D(losTex2, v_tex).rrrr;
gl_FragColor.a = mix(los1, los2, clamp(delta, 0.0, 1.0));
gl_FragColor = mix(los1, los2, clamp(delta, 0.0, 1.0));
}

View File

@ -35,7 +35,7 @@ void main()
#endif
#if MINIMAP_LOS
gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0 - texture2D(baseTex, v_tex).a);
gl_FragColor = texture2D(baseTex, v_tex).rrrr;
#endif
#if MINIMAP_POINT

View File

@ -248,25 +248,37 @@ void CLOSTexture::ConstructTexture(Renderer::Backend::IDeviceCommandContext* dev
Renderer::Backend::Sampler::Filter::LINEAR,
Renderer::Backend::Sampler::AddressMode::CLAMP_TO_EDGE);
if (backendDevice->IsFramebufferFormatSupported(Renderer::Backend::Format::R8_UNORM))
{
m_TextureFormat = Renderer::Backend::Format::R8_UNORM;
m_TextureFormatStride = 1;
}
else
{
m_TextureFormat = Renderer::Backend::Format::R8G8B8A8_UNORM;
m_TextureFormatStride = 4;
}
m_Texture = backendDevice->CreateTexture2D("LOSTexture",
Renderer::Backend::Format::A8_UNORM, textureSize, textureSize, defaultSamplerDesc);
m_TextureFormat, textureSize, textureSize, defaultSamplerDesc);
// Initialise texture with SoD color, for the areas we don't
// overwrite with uploading later.
std::unique_ptr<u8[]> texData = std::make_unique<u8[]>(textureSize * textureSize);
memset(texData.get(), 0x00, textureSize * textureSize);
const size_t textureDataSize = textureSize * textureSize * m_TextureFormatStride;
std::unique_ptr<u8[]> texData = std::make_unique<u8[]>(textureDataSize);
memset(texData.get(), 0x00, textureDataSize);
if (CRenderer::IsInitialised() && g_RenderingOptions.GetSmoothLOS())
{
m_SmoothTextures[0] = backendDevice->CreateTexture2D("LOSSmoothTexture0",
Renderer::Backend::Format::A8_UNORM, textureSize, textureSize, defaultSamplerDesc);
m_TextureFormat, textureSize, textureSize, defaultSamplerDesc);
m_SmoothTextures[1] = backendDevice->CreateTexture2D("LOSSmoothTexture1",
Renderer::Backend::Format::A8_UNORM, textureSize, textureSize, defaultSamplerDesc);
m_TextureFormat, textureSize, textureSize, defaultSamplerDesc);
m_SmoothFramebuffers[0] = backendDevice->CreateFramebuffer("LOSSmoothFramebuffer0",
m_SmoothTextures[0].get(), nullptr);
m_SmoothFramebuffers[1] = backendDevice->CreateFramebuffer("LOSSmoothFramebuffer1",
m_SmoothTextures[1].get(), nullptr);
m_SmoothFramebuffers[0] = backendDevice->CreateFramebuffer(
"LOSSmoothFramebuffer0", m_SmoothTextures[0].get(), nullptr);
m_SmoothFramebuffers[1] = backendDevice->CreateFramebuffer(
"LOSSmoothFramebuffer1", m_SmoothTextures[1].get(), nullptr);
if (!m_SmoothFramebuffers[0] || !m_SmoothFramebuffers[1])
{
LOGERROR("Failed to create LOS framebuffers");
@ -274,16 +286,13 @@ void CLOSTexture::ConstructTexture(Renderer::Backend::IDeviceCommandContext* dev
}
deviceCommandContext->UploadTexture(
m_SmoothTextures[0].get(), Renderer::Backend::Format::A8_UNORM,
texData.get(), textureSize * textureSize);
m_SmoothTextures[0].get(), m_TextureFormat, texData.get(), textureDataSize);
deviceCommandContext->UploadTexture(
m_SmoothTextures[1].get(), Renderer::Backend::Format::A8_UNORM,
texData.get(), textureSize * textureSize);
m_SmoothTextures[1].get(), m_TextureFormat, texData.get(), textureDataSize);
}
deviceCommandContext->UploadTexture(
m_Texture.get(), Renderer::Backend::Format::A8_UNORM,
texData.get(), textureSize * textureSize);
m_Texture.get(), m_TextureFormat, texData.get(), textureDataSize);
texData.reset();
@ -334,32 +343,49 @@ void CLOSTexture::RecomputeTexture(Renderer::Backend::IDeviceCommandContext* dev
PROFILE("recompute LOS texture");
size_t pitch;
const size_t dataSize = GetBitmapSize(m_MapSize, m_MapSize, &pitch);
ENSURE(pitch * m_MapSize <= dataSize);
std::unique_ptr<u8[]> losData = std::make_unique<u8[]>(dataSize);
CmpPtr<ICmpRangeManager> cmpRangeManager(m_Simulation, SYSTEM_ENTITY);
if (!cmpRangeManager)
return;
size_t pitch;
const size_t dataSize = GetBitmapSize(m_MapSize, m_MapSize, &pitch);
ENSURE(pitch * m_MapSize <= dataSize);
std::unique_ptr<u8[]> losData = std::make_unique<u8[]>(
dataSize * m_TextureFormatStride);
CLosQuerier los(cmpRangeManager->GetLosQuerier(g_Game->GetSimulation2()->GetSimContext().GetCurrentDisplayedPlayer()));
GenerateBitmap(los, &losData[0], m_MapSize, m_MapSize, pitch);
// GenerateBitmap writes data tightly packed and we need to offset it to fit
// into the texture format properly.
const size_t textureDataPitch = pitch * m_TextureFormatStride;
if (m_TextureFormatStride > 1)
{
// We skip the last byte because it will be first in our order and we
// don't need to move it.
for (size_t index = 0; index + 1 < dataSize; ++index)
{
const size_t oldAddress = dataSize - 1 - index;
const size_t newAddress = oldAddress * m_TextureFormatStride;
losData[newAddress] = losData[oldAddress];
losData[oldAddress] = 0;
}
}
if (CRenderer::IsInitialised() && g_RenderingOptions.GetSmoothLOS() && recreated)
{
deviceCommandContext->UploadTextureRegion(
m_SmoothTextures[0].get(), Renderer::Backend::Format::A8_UNORM, losData.get(),
pitch * m_MapSize, 0, 0, pitch, m_MapSize);
m_SmoothTextures[0].get(), m_TextureFormat, losData.get(),
textureDataPitch * m_MapSize, 0, 0, pitch, m_MapSize);
deviceCommandContext->UploadTextureRegion(
m_SmoothTextures[1].get(), Renderer::Backend::Format::A8_UNORM, losData.get(),
pitch * m_MapSize, 0, 0, pitch, m_MapSize);
m_SmoothTextures[1].get(), m_TextureFormat, losData.get(),
textureDataPitch * m_MapSize, 0, 0, pitch, m_MapSize);
}
deviceCommandContext->UploadTextureRegion(
m_Texture.get(), Renderer::Backend::Format::A8_UNORM, losData.get(),
pitch * m_MapSize, 0, 0, pitch, m_MapSize);
m_Texture.get(), m_TextureFormat, losData.get(),
textureDataPitch * m_MapSize, 0, 0, pitch, m_MapSize);
}
size_t CLOSTexture::GetBitmapSize(size_t w, size_t h, size_t* pitch)

View File

@ -20,6 +20,7 @@
#include "graphics/ShaderTechniquePtr.h"
#include "maths/Matrix3D.h"
#include "renderer/backend/Format.h"
#include "renderer/backend/IDeviceCommandContext.h"
#include "renderer/backend/IFramebuffer.h"
#include "renderer/backend/ITexture.h"
@ -87,6 +88,12 @@ private:
bool m_ShaderInitialized = false;
// We need to choose the smallest format. We always use the red channel but
// R8_UNORM might be unavailable on some platforms. So we fallback to
// R8G8B8A8_UNORM.
Renderer::Backend::Format m_TextureFormat =
Renderer::Backend::Format::UNDEFINED;
size_t m_TextureFormatStride = 0;
std::unique_ptr<Renderer::Backend::ITexture>
m_Texture, m_SmoothTextures[2];

View File

@ -490,11 +490,11 @@ void CMiniMapTexture::RenderFinalTexture(
DrawTexture(deviceCommandContext);
deviceCommandContext->EndPass();
pipelineStateDesc.blendState.enabled = false;
pipelineStateDesc.blendState.colorWriteMask =
Renderer::Backend::ColorWriteMask::ALPHA;
deviceCommandContext->SetGraphicsPipelineState(pipelineStateDesc);
tech = g_Renderer.GetShaderManager().LoadEffect(str_minimap_los, CShaderDefines());
deviceCommandContext->SetGraphicsPipelineState(
tech->GetGraphicsPipelineStateDesc());
deviceCommandContext->BeginPass();
shader = tech->GetShader();
deviceCommandContext->SetTexture(
shader->GetBindingSlot(str_baseTex), losTexture.GetTexture());

View File

@ -21,7 +21,6 @@
#include "graphics/Canvas2D.h"
#include "graphics/GameView.h"
#include "graphics/LOSTexture.h"
#include "graphics/MiniMapTexture.h"
#include "graphics/MiniPatch.h"
#include "graphics/ShaderManager.h"

View File

@ -127,6 +127,7 @@ X(mapSize)
X(maskTex)
X(maskTextureTransform)
X(minimap)
X(minimap_los)
X(modelViewMatrix)
X(murkiness)
X(normalMap)

View File

@ -27,12 +27,15 @@ namespace Backend
enum class Format
{
UNDEFINED,
R8_UNORM,
R8G8_UNORM,
R8G8_UINT,
R8G8B8_UNORM,
R8G8B8A8_UNORM,
R8G8B8A8_UINT,
// TODO: we need to drop legacy A8 and L8 formats as soon as we have proper
// channel swizzling.
A8_UNORM,
L8_UNORM,

View File

@ -99,6 +99,8 @@ public:
virtual bool IsTextureFormatSupported(const Format format) const = 0;
virtual bool IsFramebufferFormatSupported(const Format format) const = 0;
virtual const Capabilities& GetCapabilities() const = 0;
};

View File

@ -122,6 +122,11 @@ bool CDevice::IsTextureFormatSupported(const Format UNUSED(format)) const
return true;
}
bool CDevice::IsFramebufferFormatSupported(const Format UNUSED(format)) const
{
return true;
}
} // namespace Dummy
} // namespace Backend

View File

@ -76,6 +76,8 @@ public:
bool IsTextureFormatSupported(const Format format) const override;
bool IsFramebufferFormatSupported(const Format format) const override;
const Capabilities& GetCapabilities() const override { return m_Capabilities; }
protected:

View File

@ -912,6 +912,27 @@ bool CDevice::IsTextureFormatSupported(const Format format) const
return supported;
}
bool CDevice::IsFramebufferFormatSupported(const Format format) const
{
bool supported = false;
switch (format)
{
case Format::UNDEFINED:
break;
#if !CONFIG2_GLES
case Format::R8_UNORM:
supported = ogl_HaveVersion(3, 0);
break;
#endif
case Format::R8G8B8A8_UNORM:
supported = true;
break;
default:
break;
}
return supported;
}
} // namespace GL
} // namespace Backend

View File

@ -93,6 +93,8 @@ public:
bool IsTextureFormatSupported(const Format format) const override;
bool IsFramebufferFormatSupported(const Format format) const override;
const Capabilities& GetCapabilities() const override { return m_Capabilities; }
private:

View File

@ -228,6 +228,9 @@ void CDeviceCommandContext::UploadTextureRegion(
ENSURE(layer == 0);
if (texture->GetFormat() == Format::R8G8B8A8_UNORM ||
texture->GetFormat() == Format::R8G8B8_UNORM ||
#if !CONFIG2_GLES
texture->GetFormat() == Format::R8_UNORM ||
#endif
texture->GetFormat() == Format::A8_UNORM)
{
ENSURE(texture->GetFormat() == dataFormat);
@ -241,6 +244,12 @@ void CDeviceCommandContext::UploadTextureRegion(
pixelFormat = GL_RGB;
bytesPerPixel = 3;
break;
#if !CONFIG2_GLES
case Format::R8_UNORM:
pixelFormat = GL_RED;
bytesPerPixel = 1;
break;
#endif
case Format::A8_UNORM:
pixelFormat = GL_ALPHA;
bytesPerPixel = 1;
@ -980,7 +989,14 @@ void CDeviceCommandContext::DrawInstanced(
return;
ENSURE(firstInstance == 0);
m_ShaderProgram->AssertPointersBound();
#if CONFIG2_GLES
ENSURE(!m_Device->GetCapabilities().instancing);
UNUSED2(firstVertex);
UNUSED2(vertexCount);
UNUSED2(instanceCount);
#else
glDrawArraysInstancedARB(GL_TRIANGLES, firstVertex, vertexCount, instanceCount);
#endif
ogl_WarnIfError();
}
@ -1004,9 +1020,16 @@ void CDeviceCommandContext::DrawIndexedInstanced(
// Don't use glMultiDrawElements here since it doesn't have a significant
// performance impact and it suffers from various driver bugs (e.g. it breaks
// in Mesa 7.10 swrast with index VBOs).
#if CONFIG2_GLES
ENSURE(!m_Device->GetCapabilities().instancing);
UNUSED2(indexCount);
UNUSED2(firstIndex);
UNUSED2(instanceCount);
#else
glDrawElementsInstancedARB(GL_TRIANGLES, indexCount, GL_UNSIGNED_SHORT,
static_cast<const void*>((static_cast<const uint8_t*>(m_IndexBufferData) + sizeof(uint16_t) * firstIndex)),
instanceCount);
#endif
ogl_WarnIfError();
}

View File

@ -57,6 +57,8 @@ std::unique_ptr<CFramebuffer> CFramebuffer::Create(
if (colorAttachment)
{
ENSURE(device->IsFramebufferFormatSupported(colorAttachment->GetFormat()));
framebuffer->m_AttachmentMask |= GL_COLOR_BUFFER_BIT;
#if CONFIG2_GLES

View File

@ -1101,12 +1101,17 @@ public:
const GLboolean normalized = NormalizedFromFormat(format);
glVertexAttribPointer(
attributeLocation, size, type, normalized, stride, static_cast<const u8*>(data) + offset);
#if CONFIG2_GLES
ENSURE(!m_Device->GetCapabilities().instancing);
UNUSED2(rate);
#else
if (rate == VertexAttributeRate::PER_INSTANCE)
ENSURE(m_Device->GetCapabilities().instancing);
if (m_Device->GetCapabilities().instancing)
{
glVertexAttribDivisorARB(attributeLocation, rate == VertexAttributeRate::PER_INSTANCE ? 1 : 0);
}
#endif
m_ValidStreams |= GetStreamMask(stream);
}

View File

@ -178,6 +178,13 @@ std::unique_ptr<CTexture> CTexture::Create(CDevice* device, const char* name,
pixelFormat = GL_RGB;
pixelType = GL_UNSIGNED_BYTE;
break;
#if !CONFIG2_GLES
case Format::R8_UNORM:
internalFormat = GL_RED;
pixelFormat = GL_RED;
pixelType = GL_UNSIGNED_BYTE;
break;
#endif
case Format::A8_UNORM:
internalFormat = GL_ALPHA;
pixelFormat = GL_ALPHA;

View File

@ -162,6 +162,12 @@ bool CDevice::IsTextureFormatSupported(const Format format) const
return false;
}
bool CDevice::IsFramebufferFormatSupported(const Format format) const
{
UNUSED2(format);
return false;
}
} // namespace Vulkan
} // namespace Backend

View File

@ -81,6 +81,8 @@ public:
bool IsTextureFormatSupported(const Format format) const override;
bool IsFramebufferFormatSupported(const Format format) const override;
const Capabilities& GetCapabilities() const override { return m_Capabilities; }
private: