mirror of
https://github.com/SpartanJ/eepp.git
synced 2026-10-01 18:50:16 +03:00
Single pass subpixel rendering in all backends.
Build fixes.
This commit is contained in:
@@ -262,6 +262,7 @@ void Renderer::init() {
|
||||
writeExtension( EEGL_EXT_blend_subtract, GLEW_EXT_blend_subtract );
|
||||
writeExtension( EEGL_ARB_blend_func_extended,
|
||||
GLEW_ARB_blend_func_extended || GLEW_VERSION_3_3 );
|
||||
writeExtension( EEGL_EXT_blend_func_extended, GLEW_EXT_blend_func_extended );
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
@@ -301,6 +302,10 @@ void Renderer::init() {
|
||||
writeExtension( EEGL_ARB_blend_func_extended,
|
||||
!is_es &&
|
||||
( glVersion >= 330 || isExtension( "GL_ARB_blend_func_extended" ) ) );
|
||||
writeExtension( EEGL_EXT_blend_func_extended,
|
||||
is_es && ( isExtension( "GL_EXT_blend_func_extended" ) ||
|
||||
isExtension( "GL_WEBGL_blend_func_extended" ) ||
|
||||
isExtension( "WEBGL_blend_func_extended" ) ) );
|
||||
}
|
||||
|
||||
// NVIDIA added support for GL_OES_compressed_ETC1_RGB8_texture in desktop GPUs
|
||||
@@ -587,6 +592,14 @@ bool Renderer::bindFragDataLocationIndexed( unsigned int program, unsigned int c
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
static pglBindFragDataLocationIndexed bindFragDataLocationIndexedEXT = NULL;
|
||||
if ( NULL == bindFragDataLocationIndexedEXT )
|
||||
bindFragDataLocationIndexedEXT =
|
||||
(pglBindFragDataLocationIndexed)getProcAddress( "glBindFragDataLocationIndexedEXT" );
|
||||
if ( NULL != bindFragDataLocationIndexedEXT ) {
|
||||
bindFragDataLocationIndexedEXT( program, colorNumber, index, name );
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -115,6 +115,21 @@ void RendererGL3::reloadCurrentShader() {
|
||||
reloadShader( mCurShader );
|
||||
}
|
||||
|
||||
ShaderProgramPtr RendererGL3::createSubpixelDualSourceShader() {
|
||||
std::string vertexShader = mBaseVertexShader;
|
||||
String::replaceAll( vertexShader, "#version 120", "#version 130" );
|
||||
return RendererGLShader::createSubpixelDualSourceShader(
|
||||
vertexShader, subpixelDualSourceFragmentShaderGLSL130(), true );
|
||||
}
|
||||
|
||||
bool RendererGL3::canUseSubpixelDualSourceShader() const {
|
||||
for ( Int32 state : mPlanesStates ) {
|
||||
if ( state != 0 )
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void RendererGL3::reloadShader( ShaderProgram* Shader ) {
|
||||
mCurShader = NULL;
|
||||
|
||||
|
||||
@@ -147,6 +147,32 @@ void RendererGL3CP::reloadCurrentShader() {
|
||||
reloadShader( mCurShader );
|
||||
}
|
||||
|
||||
ShaderProgramPtr RendererGL3CP::createSubpixelDualSourceShader() {
|
||||
static const char fragmentShader[] = R"(#version 330
|
||||
uniform sampler2D textureUnit0;
|
||||
in vec4 dgl_Color;
|
||||
in vec4 dgl_TexCoord[1];
|
||||
out vec4 dgl_FragColor;
|
||||
out vec4 dgl_FragCoverage;
|
||||
void main() {
|
||||
vec3 coverage = texture( textureUnit0, dgl_TexCoord[0].xy ).rgb;
|
||||
float meanCoverage = dot( coverage, vec3( 1.0 / 3.0 ) );
|
||||
dgl_FragColor = vec4( dgl_Color.rgb, dgl_Color.a * meanCoverage );
|
||||
dgl_FragCoverage = vec4( dgl_Color.a * coverage, 0.0 );
|
||||
}
|
||||
)";
|
||||
return RendererGLShader::createSubpixelDualSourceShader( mBaseVertexShader, fragmentShader,
|
||||
true );
|
||||
}
|
||||
|
||||
bool RendererGL3CP::canUseSubpixelDualSourceShader() const {
|
||||
for ( Int32 state : mPlanesStates ) {
|
||||
if ( state != 0 )
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void RendererGL3CP::reloadShader( ShaderProgram* Shader ) {
|
||||
mCurShader = NULL;
|
||||
|
||||
|
||||
@@ -148,6 +148,35 @@ void RendererGLES2::reloadCurrentShader() {
|
||||
reloadShader( mCurShader );
|
||||
}
|
||||
|
||||
ShaderProgramPtr RendererGLES2::createSubpixelDualSourceShader() {
|
||||
#ifdef EE_GLES2
|
||||
static const char fragmentShader[] = R"(#extension GL_EXT_blend_func_extended : require
|
||||
precision mediump float;
|
||||
precision lowp int;
|
||||
uniform sampler2D textureUnit0;
|
||||
varying vec4 dgl_Color;
|
||||
varying mediump vec4 dgl_TexCoord[1];
|
||||
void main() {
|
||||
vec3 coverage = texture2D( textureUnit0, dgl_TexCoord[0].xy ).rgb;
|
||||
float meanCoverage = dot( coverage, vec3( 1.0 / 3.0 ) );
|
||||
gl_FragColor = vec4( dgl_Color.rgb, dgl_Color.a * meanCoverage );
|
||||
gl_SecondaryFragColorEXT = vec4( dgl_Color.a * coverage, 0.0 );
|
||||
}
|
||||
)";
|
||||
return RendererGLShader::createSubpixelDualSourceShader( mBaseVertexShader, fragmentShader,
|
||||
false );
|
||||
#else
|
||||
std::string vertexShader = mBaseVertexShader;
|
||||
String::replaceAll( vertexShader, "#version 120", "#version 130" );
|
||||
return RendererGLShader::createSubpixelDualSourceShader(
|
||||
vertexShader, subpixelDualSourceFragmentShaderGLSL130(), true );
|
||||
#endif
|
||||
}
|
||||
|
||||
bool RendererGLES2::canUseSubpixelDualSourceShader() const {
|
||||
return !mClippingEnabled && !mPointSpriteEnabled;
|
||||
}
|
||||
|
||||
void RendererGLES2::reloadShader( ShaderProgram* Shader ) {
|
||||
mCurShader = NULL;
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include <eepp/graphics/blendmode.hpp>
|
||||
#include <eepp/graphics/renderer/openglext.hpp>
|
||||
#include <eepp/graphics/renderer/rendererglshader.hpp>
|
||||
#include <eepp/graphics/renderer/rendererstackhelper.hpp>
|
||||
@@ -24,6 +25,65 @@ RendererGLShader::~RendererGLShader() {
|
||||
eeSAFE_DELETE( mStack );
|
||||
}
|
||||
|
||||
const char* RendererGLShader::subpixelDualSourceFragmentShaderGLSL130() {
|
||||
return R"(#version 130
|
||||
uniform sampler2D textureUnit0;
|
||||
varying vec4 dgl_Color;
|
||||
varying vec4 dgl_TexCoord[1];
|
||||
out vec4 dgl_FragColor;
|
||||
out vec4 dgl_FragCoverage;
|
||||
void main() {
|
||||
vec3 coverage = texture2D( textureUnit0, dgl_TexCoord[0].xy ).rgb;
|
||||
float meanCoverage = dot( coverage, vec3( 1.0 / 3.0 ) );
|
||||
dgl_FragColor = vec4( dgl_Color.rgb, dgl_Color.a * meanCoverage );
|
||||
dgl_FragCoverage = vec4( dgl_Color.a * coverage, 0.0 );
|
||||
}
|
||||
)";
|
||||
}
|
||||
|
||||
ShaderProgramPtr RendererGLShader::createSubpixelDualSourceShader(
|
||||
const std::string& vertexShader, const std::string& fragmentShader, bool bindFragmentOutputs ) {
|
||||
ShaderProgramPtr shader = ShaderProgram::New( "eepp-subpixel-dual-source-text" );
|
||||
const bool shaderEnsure = Shader::ensure();
|
||||
Shader::ensure( false );
|
||||
ShaderPtr vs( eeNew( VertexShader, ( vertexShader.c_str(), vertexShader.size() ) ),
|
||||
ResourceDeleter<Shader>() );
|
||||
ShaderPtr fs( eeNew( FragmentShader, ( fragmentShader.c_str(), fragmentShader.size() ) ),
|
||||
ResourceDeleter<Shader>() );
|
||||
Shader::ensure( shaderEnsure );
|
||||
if ( !shader || !vs->isValid() || !fs->isValid() )
|
||||
return {};
|
||||
shader->addShader( std::move( vs ) );
|
||||
shader->addShader( std::move( fs ) );
|
||||
if ( bindFragmentOutputs &&
|
||||
( !shader->bindFragDataLocationIndexed( 0, 0, "dgl_FragColor" ) ||
|
||||
!shader->bindFragDataLocationIndexed( 0, 1, "dgl_FragCoverage" ) ) )
|
||||
return {};
|
||||
return shader->link() ? shader : ShaderProgramPtr{};
|
||||
}
|
||||
|
||||
bool RendererGLShader::drawSubpixelDualSourceArrays( unsigned int mode, int first, int count ) {
|
||||
if ( !isExtension( EEGL_ARB_blend_func_extended ) &&
|
||||
!isExtension( EEGL_EXT_blend_func_extended ) )
|
||||
return false;
|
||||
if ( !canUseSubpixelDualSourceShader() )
|
||||
return false;
|
||||
if ( !mSubpixelDualSourceShader && !mSubpixelDualSourceShaderInitializationAttempted ) {
|
||||
mSubpixelDualSourceShaderInitializationAttempted = true;
|
||||
mSubpixelDualSourceShader = createSubpixelDualSourceShader();
|
||||
}
|
||||
if ( !mSubpixelDualSourceShader )
|
||||
return false;
|
||||
ShaderProgram* previousShader = mCurShader;
|
||||
const BlendMode previousBlendMode = BlendMode::getPreBlendFunc();
|
||||
setShader( mSubpixelDualSourceShader.get() );
|
||||
blendFuncSeparate( GL_SRC1_COLOR, GL_ONE_MINUS_SRC1_COLOR, GL_ONE, GL_ONE_MINUS_SRC_ALPHA );
|
||||
drawArrays( mode, first, count );
|
||||
BlendMode::setMode( previousBlendMode, true );
|
||||
setShader( previousShader );
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RendererGLShader::setTextureColorMode( Int32 mode ) {
|
||||
if ( mTextureColorMode_id == -1 )
|
||||
return false;
|
||||
|
||||
@@ -1811,11 +1811,11 @@ void Text::draw( const Float& X, const Float& Y, const Vector2f& scale, const Fl
|
||||
const bool allowSubpixel = effect == BlendMode::Alpha() && rotation == 0.f && scale == 1.f;
|
||||
|
||||
Uint32 alloc = numvert * sizeof( VertexCoords );
|
||||
Uint32 allocC = numvert * GLi->quadVertex();
|
||||
|
||||
if ( 0 != mFontStyleConfig.OutlineThickness ) {
|
||||
GLi->colorPointer( 4, GL_UNSIGNED_BYTE, 0,
|
||||
reinterpret_cast<const char*>( outlineColors.data() ), allocC );
|
||||
reinterpret_cast<const char*>( outlineColors.data() ),
|
||||
outlineColors.size() * sizeof( Color ) );
|
||||
GLi->texCoordPointer( 2, GL_FP, sizeof( VertexCoords ),
|
||||
reinterpret_cast<char*>( &mOutlineVertices[0] ), alloc );
|
||||
GLi->vertexPointer( 2, GL_FP, sizeof( VertexCoords ),
|
||||
@@ -1826,7 +1826,7 @@ void Text::draw( const Float& X, const Float& Y, const Vector2f& scale, const Fl
|
||||
}
|
||||
|
||||
GLi->colorPointer( 4, GL_UNSIGNED_BYTE, 0, reinterpret_cast<const char*>( colors.data() ),
|
||||
allocC );
|
||||
colors.size() * sizeof( Color ) );
|
||||
GLi->texCoordPointer( 2, GL_FP, sizeof( VertexCoords ),
|
||||
reinterpret_cast<char*>( &mVertices[0] ), alloc );
|
||||
GLi->vertexPointer( 2, GL_FP, sizeof( VertexCoords ),
|
||||
|
||||
@@ -167,6 +167,59 @@ UTEST( FontRendering, subpixelCoverageCompositesPerChannel ) {
|
||||
"Subpixel text did not update a transparent target's alpha" );
|
||||
}
|
||||
|
||||
#if EE_PLATFORM == EE_PLATFORM_LINUX
|
||||
UTEST( FontRendering, subpixelCoverageAllRenderers ) {
|
||||
struct RendererCase {
|
||||
GraphicsLibraryVersion version;
|
||||
const char* name;
|
||||
};
|
||||
static constexpr RendererCase renderers[] = { { GLv_2, "OpenGL 2" },
|
||||
{ GLv_3, "OpenGL 3" },
|
||||
{ GLv_3CP, "OpenGL 3 Core" },
|
||||
{ GLv_ES2, "OpenGL ES 2" } };
|
||||
|
||||
for ( const RendererCase& renderer : renderers ) {
|
||||
UIApplication app(
|
||||
WindowSettings( 320, 96, renderer.name, WindowStyle::Default, WindowBackend::Default,
|
||||
32 ),
|
||||
UIApplication::Settings( Sys::getProcessPath() + ".." + FileSystem::getOSSlash(), 1 ),
|
||||
ContextSettings( false, 0, 0, renderer.version ) );
|
||||
ASSERT_TRUE_MSG( app.getWindow() && app.getWindow()->isOpen(), renderer.name );
|
||||
ResourceScope& scope = *app.getUI()->getResourceScope();
|
||||
FontTrueTypePtr font = FontTrueType::New( renderer.name, scope );
|
||||
ASSERT_TRUE_MSG(
|
||||
font->loadFromFile( Sys::getProcessPath() + "../assets/fonts/NotoSans-Regular.ttf" ),
|
||||
renderer.name );
|
||||
font->setAntialiasing( FontAntialiasing::Subpixel );
|
||||
|
||||
EE::Window::Window* window = app.getWindow();
|
||||
window->setClearColor( Color::White );
|
||||
window->clear();
|
||||
Text::draw( String( "Direct LCD" ), { 8.f, 4.f }, font.get(), 24, Color::Black );
|
||||
Text retained( "Retained LCD", font.get(), 24 );
|
||||
retained.setFillColor( Color::Black );
|
||||
retained.draw( 8.f, 44.f );
|
||||
|
||||
Image image = window->getFrontBufferImage();
|
||||
auto hasColoredCoverage = [&image]( Uint32 top, Uint32 bottom ) {
|
||||
for ( Uint32 y = top; y < bottom; ++y ) {
|
||||
for ( Uint32 x = 0; x < image.getWidth(); ++x ) {
|
||||
const Color pixel = image.getPixel( x, y );
|
||||
const Int32 redGreenDelta = static_cast<Int32>( pixel.r ) - pixel.g;
|
||||
const Int32 greenBlueDelta = static_cast<Int32>( pixel.g ) - pixel.b;
|
||||
if ( ( redGreenDelta < 0 ? -redGreenDelta : redGreenDelta ) > 3 ||
|
||||
( greenBlueDelta < 0 ? -greenBlueDelta : greenBlueDelta ) > 3 )
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
EXPECT_TRUE_MSG( hasColoredCoverage( 0, 40 ), renderer.name );
|
||||
EXPECT_TRUE_MSG( hasColoredCoverage( 40, image.getHeight() ), renderer.name );
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
UTEST( FontRendering, scaledSubpixelGlyphAtlas ) {
|
||||
UIApplication app(
|
||||
WindowSettings( 256, 64, "eepp - Scaled Subpixel Glyph Atlas", VisualTestWindowStyle,
|
||||
|
||||
Reference in New Issue
Block a user