diff --git a/Common/GPU/D3D11/thin3d_d3d11.cpp b/Common/GPU/D3D11/thin3d_d3d11.cpp index 6dd922f1c1..09ffd4fa6a 100644 --- a/Common/GPU/D3D11/thin3d_d3d11.cpp +++ b/Common/GPU/D3D11/thin3d_d3d11.cpp @@ -263,6 +263,7 @@ D3D11DrawContext::D3D11DrawContext(ID3D11Device *device, ID3D11DeviceContext *de caps_.framebufferStencilBlitSupported = false; caps_.framebufferDepthCopySupported = true; caps_.framebufferSeparateDepthCopySupported = false; // Though could be emulated with a draw. + caps_.textureDepthSupported = true; caps_.texture3DSupported = true; caps_.fragmentShaderInt32Supported = true; caps_.anisoSupported = true; diff --git a/Common/GPU/D3D9/thin3d_d3d9.cpp b/Common/GPU/D3D9/thin3d_d3d9.cpp index e420646a40..cf73de79a3 100644 --- a/Common/GPU/D3D9/thin3d_d3d9.cpp +++ b/Common/GPU/D3D9/thin3d_d3d9.cpp @@ -682,6 +682,7 @@ D3D9Context::D3D9Context(IDirect3D9 *d3d, IDirect3D9Ex *d3dEx, int adapterId, ID HRESULT fboINTZ = d3d->CheckDeviceFormat(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, D3DFMT_A8R8G8B8, D3DUSAGE_DEPTHSTENCIL, D3DRTYPE_TEXTURE, FOURCC_INTZ); supportsINTZ = SUCCEEDED(displayINTZ) && SUCCEEDED(fboINTZ); } + caps_.textureDepthSupported = supportsINTZ; shaderLanguageDesc_.Init(HLSL_D3D9); diff --git a/Common/GPU/OpenGL/GLQueueRunner.cpp b/Common/GPU/OpenGL/GLQueueRunner.cpp index 7a9e29653e..3d3c30e433 100644 --- a/Common/GPU/OpenGL/GLQueueRunner.cpp +++ b/Common/GPU/OpenGL/GLQueueRunner.cpp @@ -758,9 +758,7 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step, bool first, bool last } GLRProgram *curProgram = nullptr; - int activeSlot = 0; - if (first) - glActiveTexture(GL_TEXTURE0 + activeSlot); + int activeSlot = -1; // State filtering tracking. int attrMask = 0; @@ -1037,15 +1035,17 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step, bool first, bool last activeSlot = slot; } if (c.bind_fb_texture.aspect == GL_COLOR_BUFFER_BIT) { - if (curTex[slot] != &c.bind_fb_texture.framebuffer->color_texture) + if (curTex[slot] != &c.bind_fb_texture.framebuffer->color_texture) { glBindTexture(GL_TEXTURE_2D, c.bind_fb_texture.framebuffer->color_texture.texture); - curTex[slot] = &c.bind_fb_texture.framebuffer->color_texture; + curTex[slot] = &c.bind_fb_texture.framebuffer->color_texture; + } } else if (c.bind_fb_texture.aspect == GL_DEPTH_BUFFER_BIT) { - if (curTex[slot] != &c.bind_fb_texture.framebuffer->z_stencil_texture) + if (curTex[slot] != &c.bind_fb_texture.framebuffer->z_stencil_texture) { glBindTexture(GL_TEXTURE_2D, c.bind_fb_texture.framebuffer->z_stencil_texture.texture); - curTex[slot] = &c.bind_fb_texture.framebuffer->z_stencil_texture; + curTex[slot] = &c.bind_fb_texture.framebuffer->z_stencil_texture; + } } else { - // TODO: Stencil texturing? + // Can't texture from stencil buffers. curTex[slot] = nullptr; } CHECK_GL_ERROR_IF_DEBUG(); @@ -1202,8 +1202,13 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step, bool first, bool last } case GLRRenderCommand::TEXTURE_SUBIMAGE: { - GLRTexture *tex = c.texture_subimage.texture; + GLint slot = c.texture_subimage.slot; + if (slot != activeSlot) { + glActiveTexture(GL_TEXTURE0 + slot); + activeSlot = slot; + } // TODO: Need bind? + GLRTexture *tex = c.texture_subimage.texture; if (!c.texture_subimage.data) Crash(); _assert_(tex->target == GL_TEXTURE_2D); diff --git a/Common/GPU/OpenGL/GLQueueRunner.h b/Common/GPU/OpenGL/GLQueueRunner.h index 13c5cfd701..e82b265ed1 100644 --- a/Common/GPU/OpenGL/GLQueueRunner.h +++ b/Common/GPU/OpenGL/GLQueueRunner.h @@ -148,11 +148,12 @@ struct GLRRenderData { struct { GLRTexture *texture; Draw::DataFormat format; - int level; - int x; - int y; - int width; - int height; + uint8_t slot; + uint8_t level; + uint16_t width; + uint16_t height; + uint16_t x; + uint16_t y; GLRAllocType allocType; uint8_t *data; // owned, delete[]-d } texture_subimage; diff --git a/Common/GPU/OpenGL/GLRenderManager.h b/Common/GPU/OpenGL/GLRenderManager.h index 9e4fafb50f..6be44a70ee 100644 --- a/Common/GPU/OpenGL/GLRenderManager.h +++ b/Common/GPU/OpenGL/GLRenderManager.h @@ -555,7 +555,7 @@ public: initSteps_.push_back(step); } - void TextureSubImage(GLRTexture *texture, int level, int x, int y, int width, int height, Draw::DataFormat format, uint8_t *data, GLRAllocType allocType = GLRAllocType::NEW) { + void TextureSubImage(int slot, GLRTexture *texture, int level, int x, int y, int width, int height, Draw::DataFormat format, uint8_t *data, GLRAllocType allocType = GLRAllocType::NEW) { _dbg_assert_(curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER); GLRRenderData _data{ GLRRenderCommand::TEXTURE_SUBIMAGE }; _data.texture_subimage.texture = texture; @@ -567,6 +567,7 @@ public: _data.texture_subimage.width = width; _data.texture_subimage.height = height; _data.texture_subimage.allocType = allocType; + _data.texture_subimage.slot = slot; curRenderStep_->commands.push_back(_data); } diff --git a/Common/GPU/OpenGL/thin3d_gl.cpp b/Common/GPU/OpenGL/thin3d_gl.cpp index 3088abc22f..130ba2d0fe 100644 --- a/Common/GPU/OpenGL/thin3d_gl.cpp +++ b/Common/GPU/OpenGL/thin3d_gl.cpp @@ -533,12 +533,14 @@ OpenGLContext::OpenGLContext() { } } caps_.texture3DSupported = gl_extensions.OES_texture_3D; + caps_.textureDepthSupported = gl_extensions.GLES3 || gl_extensions.OES_depth_texture; } else { if (gl_extensions.VersionGEThan(3, 3, 0)) { caps_.fragmentShaderInt32Supported = true; } caps_.preferredDepthBufferFormat = DataFormat::D24_S8; caps_.texture3DSupported = true; + caps_.textureDepthSupported = true; } caps_.dualSourceBlend = gl_extensions.ARB_blend_func_extended || gl_extensions.EXT_blend_func_extended; @@ -821,11 +823,14 @@ OpenGLTexture::OpenGLTexture(GLRenderManager *render, const TextureDesc &desc) : return; int level = 0; + int width = width_; + int height = height_; + int depth = depth_; for (auto data : desc.initData) { - SetImageData(0, 0, 0, width_, height_, depth_, level, 0, data, desc.initDataCallback); - width_ = (width_ + 1) / 2; - height_ = (height_ + 1) / 2; - depth_ = (depth_ + 1) / 2; + SetImageData(0, 0, 0, width, height, depth, level, 0, data, desc.initDataCallback); + width = (width + 1) / 2; + height = (height + 1) / 2; + depth = (depth + 1) / 2; level++; } mipLevels_ = desc.generateMips ? desc.mipLevels : level; @@ -1211,7 +1216,7 @@ bool OpenGLPipeline::LinkShaders() { } std::vector initialize; for (int i = 0; i < MAX_TEXTURE_SLOTS; ++i) { - if (i < queries.size()) { + if (i < samplers_.size()) { initialize.push_back({ &samplerLocs_[i], 0, i }); } else { samplerLocs_[i] = -1; diff --git a/Common/GPU/Vulkan/thin3d_vulkan.cpp b/Common/GPU/Vulkan/thin3d_vulkan.cpp index 9cb7393512..b7412d4221 100644 --- a/Common/GPU/Vulkan/thin3d_vulkan.cpp +++ b/Common/GPU/Vulkan/thin3d_vulkan.cpp @@ -789,6 +789,7 @@ VKContext::VKContext(VulkanContext *vulkan, bool splitSubmit) caps_.framebufferSeparateDepthCopySupported = true; // Will pretty much always be the case. caps_.preferredDepthBufferFormat = DataFormat::D24_S8; // TODO: Ask vulkan. caps_.texture3DSupported = true; + caps_.textureDepthSupported = true; caps_.fragmentShaderInt32Supported = true; caps_.textureNPOTFullySupported = true; caps_.fragmentShaderDepthWriteSupported = true; diff --git a/Common/GPU/thin3d.h b/Common/GPU/thin3d.h index ce06ec79c3..ff0fe1a083 100644 --- a/Common/GPU/thin3d.h +++ b/Common/GPU/thin3d.h @@ -543,6 +543,7 @@ struct DeviceCaps { bool fragmentShaderInt32Supported; bool textureNPOTFullySupported; bool fragmentShaderDepthWriteSupported; + bool textureDepthSupported; std::string deviceName; // The device name to use when creating the thin3d context, to get the same one. }; diff --git a/GPU/Common/Draw2D.cpp b/GPU/Common/Draw2D.cpp index b9ac114f92..c1aefbce20 100644 --- a/GPU/Common/Draw2D.cpp +++ b/GPU/Common/Draw2D.cpp @@ -230,7 +230,10 @@ Draw2DPipeline *Draw2D::Create2DPipeline(std::functionCreateGraphicsPipeline(pipelineDesc); diff --git a/GPU/Common/Draw2D.h b/GPU/Common/Draw2D.h index b9516e8d39..ad489dfce6 100644 --- a/GPU/Common/Draw2D.h +++ b/GPU/Common/Draw2D.h @@ -1,6 +1,7 @@ #pragma once #include "GPU/GPU.h" +#include "Common/GPU/Shader.h" // For framebuffer copies and similar things that just require passthrough. struct Draw2DVertex { @@ -32,7 +33,7 @@ inline RasterChannel Draw2DSourceChannel(Draw2DShader shader) { struct Draw2DPipelineInfo { RasterChannel readChannel; RasterChannel writeChannel; - bool secondTexture; + Slice samplers; }; struct Draw2DPipeline { diff --git a/GPU/Common/FragmentShaderGenerator.cpp b/GPU/Common/FragmentShaderGenerator.cpp index f278363e5f..35a0e9daf7 100644 --- a/GPU/Common/FragmentShaderGenerator.cpp +++ b/GPU/Common/FragmentShaderGenerator.cpp @@ -609,15 +609,15 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu WRITE(p, " uint depalShift = (u_depal_mask_shift_off_fmt >> 8) & 0xFFU;\n"); WRITE(p, " uint depalFmt = (u_depal_mask_shift_off_fmt >> 24) & 0x3U;\n"); WRITE(p, " float index0 = t.r;\n"); - WRITE(p, " float mul = 32.0 / 256.0;\n"); - WRITE(p, " if (depalFmt == 0) {\n"); // yes, different versions of Test Drive use different formats. Could do compile time by adding more compat flags but meh. - WRITE(p, " if (depalShift == 5) { index0 = t.g; mul = 64.0 / 256.0; }\n"); - WRITE(p, " else if (depalShift == 11) { index0 = t.b; }\n"); + WRITE(p, " float factor = 32.0 / 256.0;\n"); + WRITE(p, " if (depalFmt == 0u) {\n"); // yes, different versions of Test Drive use different formats. Could do compile time by adding more compat flags but meh. + WRITE(p, " if (depalShift == 5u) { index0 = t.g; factor = 64.0 / 256.0; }\n"); + WRITE(p, " else if (depalShift == 11u) { index0 = t.b; }\n"); WRITE(p, " } else {\n"); - WRITE(p, " if (depalShift == 5) { index0 = t.g; }\n"); - WRITE(p, " else if (depalShift == 10) { index0 = t.b; }\n"); + WRITE(p, " if (depalShift == 5u) { index0 = t.g; }\n"); + WRITE(p, " else if (depalShift == 10u) { index0 = t.b; }\n"); WRITE(p, " }\n"); - WRITE(p, " t = %s(pal, vec2(index0 * mul, 0.0));\n", compat.texture); + WRITE(p, " t = %s(pal, vec2(index0 * factor, 0.0));\n", compat.texture); } else { if (doTextureProjection) { // We don't use textureProj because we need better control and it's probably not much of a savings anyway. diff --git a/GPU/Common/TextureCacheCommon.cpp b/GPU/Common/TextureCacheCommon.cpp index aaa38bb7c7..332c0ba252 100644 --- a/GPU/Common/TextureCacheCommon.cpp +++ b/GPU/Common/TextureCacheCommon.cpp @@ -1044,6 +1044,7 @@ void TextureCacheCommon::SetTextureFramebuffer(const AttachCandidate &candidate) } if (candidate.channel == RASTER_DEPTH && !gstate_c.Supports(GPU_SUPPORTS_DEPTH_TEXTURE)) { + WARN_LOG_ONCE(ndepthtex, G3D, "Depth textures not supported, not binding"); // Flag to bind a null texture if we can't support depth textures. // Should only happen on old OpenGL. nextFramebufferTexture_ = nullptr; @@ -1890,13 +1891,14 @@ void TextureCacheCommon::ApplyTextureFramebuffer(VirtualFramebuffer *framebuffer bool depth = channel == RASTER_DEPTH; bool need_depalettize = CanDepalettize(texFormat, depth ? GE_FORMAT_DEPTH16 : framebuffer->drawnFormat); - bool useShaderDepal = framebufferManager_->GetCurrentRenderVFB() != framebuffer && !depth && !gstate_c.curTextureIs3D; + + // Shader depal is not supported during 3D texturing or depth texturing, and requires 32-bit integer instructions in the shader. + bool useShaderDepal = framebufferManager_->GetCurrentRenderVFB() != framebuffer && + !depth && + !gstate_c.curTextureIs3D && + draw_->GetDeviceCaps().fragmentShaderInt32Supported; // TODO: Implement shader depal in the fragment shader generator for D3D11 at least. - if (!draw_->GetDeviceCaps().fragmentShaderInt32Supported) { - useShaderDepal = false; - } - switch (draw_->GetShaderLanguageDesc().shaderLanguage) { case ShaderLanguage::HLSL_D3D11: case ShaderLanguage::HLSL_D3D9: @@ -1915,7 +1917,6 @@ void TextureCacheCommon::ApplyTextureFramebuffer(VirtualFramebuffer *framebuffer smoothedDepal = CanUseSmoothDepal(gstate, framebuffer->drawnFormat, clutTexture.rampLength); if (useShaderDepal) { - // Very icky conflation here of native and thin3d rendering. This will need careful work per backend in BindAsClutTexture. BindAsClutTexture(clutTexture.texture); diff --git a/GPU/Common/TextureShaderCommon.cpp b/GPU/Common/TextureShaderCommon.cpp index 694a646131..8fb8cd6fe9 100644 --- a/GPU/Common/TextureShaderCommon.cpp +++ b/GPU/Common/TextureShaderCommon.cpp @@ -213,6 +213,7 @@ Draw2DPipeline *TextureShaderCache::GetDepalettizeShader(uint32_t clutMode, GETe return Draw2DPipelineInfo{ config.bufferFormat == GE_FORMAT_DEPTH16 ? RASTER_DEPTH : RASTER_COLOR, RASTER_COLOR, + samplers }; }); delete[] buffer; diff --git a/GPU/GLES/DrawEngineGLES.cpp b/GPU/GLES/DrawEngineGLES.cpp index 96dc11ea22..2b6dd53a43 100644 --- a/GPU/GLES/DrawEngineGLES.cpp +++ b/GPU/GLES/DrawEngineGLES.cpp @@ -504,13 +504,13 @@ void TessellationDataTransferGLES::SendDataToShader(const SimpleVertex *const *p } renderManager_->BindTexture(TEX_SLOT_SPLINE_POINTS, data_tex[0]); // Position - renderManager_->TextureSubImage(data_tex[0], 0, 0, 0, size_u, size_v, Draw::DataFormat::R32G32B32A32_FLOAT, (u8 *)pos, GLRAllocType::NEW); + renderManager_->TextureSubImage(TEX_SLOT_SPLINE_POINTS, data_tex[0], 0, 0, 0, size_u, size_v, Draw::DataFormat::R32G32B32A32_FLOAT, (u8 *)pos, GLRAllocType::NEW); // Texcoord if (hasTexCoord) - renderManager_->TextureSubImage(data_tex[0], 0, size_u, 0, size_u, size_v, Draw::DataFormat::R32G32B32A32_FLOAT, (u8 *)tex, GLRAllocType::NEW); + renderManager_->TextureSubImage(TEX_SLOT_SPLINE_POINTS, data_tex[0], 0, size_u, 0, size_u, size_v, Draw::DataFormat::R32G32B32A32_FLOAT, (u8 *)tex, GLRAllocType::NEW); // Color if (hasColor) - renderManager_->TextureSubImage(data_tex[0], 0, size_u * 2, 0, size_u, size_v, Draw::DataFormat::R32G32B32A32_FLOAT, (u8 *)col, GLRAllocType::NEW); + renderManager_->TextureSubImage(TEX_SLOT_SPLINE_POINTS, data_tex[0], 0, size_u * 2, 0, size_u, size_v, Draw::DataFormat::R32G32B32A32_FLOAT, (u8 *)col, GLRAllocType::NEW); // Weight U if (prevSizeWU < weights.size_u) { @@ -521,7 +521,7 @@ void TessellationDataTransferGLES::SendDataToShader(const SimpleVertex *const *p renderManager_->FinalizeTexture(data_tex[1], 0, false); } renderManager_->BindTexture(TEX_SLOT_SPLINE_WEIGHTS_U, data_tex[1]); - renderManager_->TextureSubImage(data_tex[1], 0, 0, 0, weights.size_u * 2, 1, Draw::DataFormat::R32G32B32A32_FLOAT, (u8 *)weights.u, GLRAllocType::NONE); + renderManager_->TextureSubImage(TEX_SLOT_SPLINE_WEIGHTS_U, data_tex[1], 0, 0, 0, weights.size_u * 2, 1, Draw::DataFormat::R32G32B32A32_FLOAT, (u8 *)weights.u, GLRAllocType::NONE); // Weight V if (prevSizeWV < weights.size_v) { @@ -532,7 +532,7 @@ void TessellationDataTransferGLES::SendDataToShader(const SimpleVertex *const *p renderManager_->FinalizeTexture(data_tex[2], 0, false); } renderManager_->BindTexture(TEX_SLOT_SPLINE_WEIGHTS_V, data_tex[2]); - renderManager_->TextureSubImage(data_tex[2], 0, 0, 0, weights.size_v * 2, 1, Draw::DataFormat::R32G32B32A32_FLOAT, (u8 *)weights.v, GLRAllocType::NONE); + renderManager_->TextureSubImage(TEX_SLOT_SPLINE_WEIGHTS_V, data_tex[2], 0, 0, 0, weights.size_v * 2, 1, Draw::DataFormat::R32G32B32A32_FLOAT, (u8 *)weights.v, GLRAllocType::NONE); } void TessellationDataTransferGLES::EndFrame() { diff --git a/GPU/GLES/GPU_GLES.cpp b/GPU/GLES/GPU_GLES.cpp index ad83bd3637..cb41d2cff2 100644 --- a/GPU/GLES/GPU_GLES.cpp +++ b/GPU/GLES/GPU_GLES.cpp @@ -201,9 +201,9 @@ void GPU_GLES::CheckGPUFeatures() { features |= GPU_SUPPORTS_DEPTH_CLAMP | GPU_SUPPORTS_ACCURATE_DEPTH; // Our implementation of depth texturing needs simple Z range, so can't // use the extension hacks (yet). - if (gl_extensions.GLES3) - features |= GPU_SUPPORTS_DEPTH_TEXTURE; } + if (draw_->GetDeviceCaps().textureDepthSupported) + features |= GPU_SUPPORTS_DEPTH_TEXTURE; if (draw_->GetDeviceCaps().clipDistanceSupported) features |= GPU_SUPPORTS_CLIP_DISTANCE; if (draw_->GetDeviceCaps().cullDistanceSupported) diff --git a/GPU/GLES/ShaderManagerGLES.cpp b/GPU/GLES/ShaderManagerGLES.cpp index 858052f43e..2b865cc818 100644 --- a/GPU/GLES/ShaderManagerGLES.cpp +++ b/GPU/GLES/ShaderManagerGLES.cpp @@ -96,8 +96,9 @@ LinkedShader::LinkedShader(GLRenderManager *render, VShaderID VSID, Shader *vs, std::vector queries; queries.push_back({ &u_tex, "tex" }); - queries.push_back({ &u_proj, "u_proj" }); - queries.push_back({ &u_proj_through, "u_proj_through" }); + queries.push_back({ &u_pal, "pal" }); + queries.push_back({ &u_testtex, "testtex" }); + queries.push_back({ &u_fbotex, "fbotex" }); queries.push_back({ &u_proj, "u_proj" }); queries.push_back({ &u_proj_through, "u_proj_through" }); @@ -108,13 +109,9 @@ LinkedShader::LinkedShader(GLRenderManager *render, VShaderID VSID, Shader *vs, queries.push_back({ &u_alphacolormask, "u_alphacolormask" }); queries.push_back({ &u_colorWriteMask, "u_colorWriteMask" }); queries.push_back({ &u_stencilReplaceValue, "u_stencilReplaceValue" }); - queries.push_back({ &u_testtex, "testtex" }); - - queries.push_back({ &u_fbotex, "fbotex" }); queries.push_back({ &u_blendFixA, "u_blendFixA" }); queries.push_back({ &u_blendFixB, "u_blendFixB" }); queries.push_back({ &u_fbotexSize, "u_fbotexSize" }); - queries.push_back({ &u_pal, "pal" }); // Transform queries.push_back({ &u_view, "u_view" }); diff --git a/GPU/GLES/TextureCacheGLES.cpp b/GPU/GLES/TextureCacheGLES.cpp index 97e51dcd6c..61683e3ac3 100644 --- a/GPU/GLES/TextureCacheGLES.cpp +++ b/GPU/GLES/TextureCacheGLES.cpp @@ -218,6 +218,11 @@ void TextureCacheGLES::UpdateCurrentClut(GEPaletteFormat clutFormat, u32 clutBas } void TextureCacheGLES::BindTexture(TexCacheEntry *entry) { + if (!entry) { + render_->BindTexture(0, nullptr); + lastBoundTexture = nullptr; + return; + } if (entry->textureName != lastBoundTexture) { render_->BindTexture(0, entry->textureName); lastBoundTexture = entry->textureName; diff --git a/UI/OnScreenDisplay.cpp b/UI/OnScreenDisplay.cpp index 1b4700e9f6..acc0347fd1 100644 --- a/UI/OnScreenDisplay.cpp +++ b/UI/OnScreenDisplay.cpp @@ -28,18 +28,33 @@ void OnScreenMessagesView::Draw(UIContext &dc) { float alpha = (iter->endTime - now) * 4.0f; if (alpha > 1.0) alpha = 1.0f; if (alpha < 0.0) alpha = 0.0f; + dc.SetFontScale(1.0f, 1.0f); // Messages that are wider than the screen are left-aligned instead of centered. + + int align = 0; + // Simple heuristic: Draw messages that contain line breaks with ASCII_FONT (likely debug output) + if (iter->text.find('\n') != 0) { + align |= FLAG_DYNAMIC_ASCII; + } + float tw, th; - dc.MeasureText(dc.theme->uiFont, 1.0f, 1.0f, iter->text.c_str(), &tw, &th); + dc.MeasureText(dc.theme->uiFont, 1.0f, 1.0f, iter->text.c_str(), &tw, &th, align); float x = bounds_.centerX(); - int align = ALIGN_TOP | ALIGN_HCENTER; if (tw > bounds_.w) { - align = ALIGN_TOP | ALIGN_LEFT; + align |= ALIGN_TOP | ALIGN_LEFT; x = 2; + } else { + align |= ALIGN_TOP | ALIGN_HCENTER; + } + float scale = 1.0f; + if (th > bounds_.h - y) { + // Scale down! + scale = std::max(0.15f, (bounds_.h - y) / th); + dc.SetFontScale(scale, scale); } dc.SetFontStyle(dc.theme->uiFont); dc.DrawTextShadow(iter->text.c_str(), x, y, colorAlpha(iter->color, alpha), align); - y += h; + y += th * scale; } osm.Unlock();